我想实现如下的场景,判断当前Android手机上是否正在播放音乐,如果是,通过某个特定的手势,
或者点击某个按键,将当前我正在听的音乐共享出去。
第一步,就是判断当前是否有音乐正在播放。
最开始我想得有点复杂,以为要深入framework或更下层去做手脚才行,找了一下资料,发现AudioManager对外暴露了接口。
[java]  
/** Checks whether any music is active. */  
isMusicActive()  
通过这个接口就可以判断当前系统是否有音乐在播放了。
 
还有一个问题,如果我想在音乐一开始就已经播放的时候,就知道这个事件,以便进行特殊的处理。
再进一步看一下 AudioManager 的
源码,发现其中有如下方法:
 
[java] 
    /** 
     *  Request audio focus. 
     *  Send a request to obtain the audio focus 
     *  @param l the listener to be notified of audio focus changes 
     *  @param streamType the main audio stream type affected by the focus request 
     *  @param durationHint use {@link #AUDIOFOCUS_GAIN_TRANSIENT} to indicate this focus request 
     *      is temporary, and focus will be abandonned shortly. Examples of transient requests are 
     *      for the playback of driving directions, or notifications sounds. 
     *      Use {@link #AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK} to indicate also that it's ok for 
     *      the previous focus owner to keep playing if it ducks its audio output. 
     *      Use {@link #AUDIOFOCUS_GAIN} for a focus request of unknown duration such 
     *      as the playback of a song or a video. 
     *  @return {@link #AUDIOFOCUS_REQUEST_FAILED} or {@link #AUDIOFOCUS_REQUEST_GRANTED} 
     */  
    public int requestAudioFocus(OnAudioFocusChangeListener l, int streamType, int durationHint)  
 
从字面意思来看:请求音频焦点,再看这个函数的返回值:
[java] 
    /** 
     * A failed focus change request. 
     */  
    public static final int AUDIOFOCUS_REQUEST_FAILED = 0;  
  
  
    /** 
     * A successful focus change request. 
     */  
    public static final int AUDIOFOCUS_REQUEST_GRANTED = 1;  
 
这个函数可能对我有帮助,进一步查一下Google官方的帮助:http://developer.android.com/training/managing-audio/audio-focus.
html 
Managing Audio Focus
With multiple apps potentially playing audio it's important to think about how they should interact. To avoid every music app playing at the same time, 
Android uses audio focus to moderate audio playback—only apps that hold the audio focus should play audio.
 
Before your app starts playing audio it should request—and receive—the audio focus.  Likewise, it should know how to listen for a loss of audio focus and respond appropriately when that happens.
 
简单地翻译一下:
管理音频焦点
多个应用都在播放音频的可能性,所以考虑应用间如何交互非常重要。为避免每个音乐应用同时播放,Android使用音频焦点来协调音频的播放----只有获取到音频焦点的应用可以播放音频。
在你的应用开始播放音频之前,它应该先请求--并接收音频焦点。同样,它也应该知道当监听到失去音频焦点后如何合理地进行响应。
 
沿着这个路应该是对的,写了下面的测试代码进行验证。这个主要是Service的实现,你还需要实现一个Activity去启动Service、结束Service:
[java]  
package com.example.servicetest;  
  
import android.app.Service;  
import android.content.Context;  
import android.content.Intent;  
import android.media.AudioManager;  
import android.media.AudioManager.OnAudioFocusChangeListener;  
import android.media.MediaPlayer;  
import android.os.IBinder;  
import android.util.Log;  
import android.widget.Toast;  
  
public class MainService extends Service  
{  
    private static final String TAG = "MainService";  
      
    private MediaPlayer player;  
      
    private AudioManager mAm;  
      
    private MyOnAudioFocusChangeListener mListener;  
  
      
    @Override  
    public void onCreate()  
    {  
        Log.i(TAG, "onCreate");  
          
        player = MediaPlayer.create(this, R.raw.test);  // 在res目录下新建raw目录,复制一个test.mp3文件到此目录下。  
        player.setLooping(false);  
          
        mAm = (AudioManager) getApplicationContext().getSystemService(Context.AUDIO_SERVICE);  
        mListener = new MyOnAudioFocusChangeListener();  
    }  
      
  
    @Override  
    public IBinder onBind(Intent intent)  
    {  
        return null;  
    }  
      
  
    @Override  
    public void onStart(Intent intent, int startid)  
    {  
        Toast.makeText(this, "My Service Start", Toast.LENGTH_LONG).show();  
        Log.i(TAG, "onStart");  
          
        // Request audio focus for playback  
        int result = mAm.requestAudioFocus(mListener,  
                AudioManager.STREAM_MUSIC,  
                AudioManager.AUDIOFOCUS_GAIN);  
          
        if (result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED)  
        {  
            Log.i(TAG, "requestAudioFocus successfully.");  
              
            // Start playback.  
            player.start();  
        }  
        else  
        {  
            Log.e(TAG, "requestAudioFocus failed.");  
        }  
    }  
      
  
    @Override  
    public void onDestroy()  
    {  
        Toast.makeText(this, "My Service Stoped", Toast.LENGTH_LONG).show();  
        Log.i(TAG, "onDestroy");  
        player.stop();  
          
        mAm.abandonAudioFocus(mListener);  
    }  
      
  
    private class MyOnAudioFocusChangeListener implements  
            OnAudioFocusChangeListener  
    {  
        @Override  
        public void onAudioFocusChange(int focusChange)  
        {  
            Log.i(TAG, "focusChange=" + focusChange);  
        }  
    }  
}  
 
和 天天动听 结合起来测试,先打开天天动听播放音乐,再启动这个Service,发现天天动听自动暂停,再停止这个Service,天天动听又开始播放了。
反过来,我先启动这个Service,再播放、暂停天天动听,“Log.i(TAG, "focusChange=" + focusChange);” 这个确实有输出日志。
 
主流的音乐播放器,都遵循此规则的,所以通过使用Android的这个机制,我们就可以监控音乐的播放了。
 
还有一个问题,如何知道当前播放的音乐信息呢?两个思路:
1、通过在后台自动截取音频流的输出,通过服务器进行听歌识曲;
2、通过在SystemUI中拦截主流音乐播放器的通知;
 
第1个思路,从原理上是可行的,但是实现起来难度比较大,而且严重依赖网络;
还是先来分析一下第2个思路。
先找主流的Android音乐播放器来做个简单地测试,比如:天天动听、QQ音乐、酷狗音乐、酷我音乐、百度音乐等,在播放过程中,都会向状态 栏中发一个Notification消息,其中已经包含歌曲信息。那我只需要做一个特殊的拦截并进行包名匹配,就可以获取正在播放的音乐了。
 
具体思路如下:
1、实现一个服务,这个服务在Android手机启动时,自动运行起来,通过 AudioManager.requestAudioFocus() 获取音频焦点,但什么事都不干,只为有其它音乐播放器开始运行时,得到一个通知消息;
2、修改SystemUI,当主流音乐播放器发Notification到状态栏时,从中获取到音乐信息;
3、步骤1的Listener就可以集成到SystemUI中,这样当音乐焦点被其它音乐播放器抢走后,再结合最近收到的Notification通知,这样更准确一些;
												
												
								- Android: 判断当前手机品牌(转)
		
参考资料 Android判断手机ROM 正文 有时候需要判断手机系统的ROM,检测ROM是MIUI.EMUI还是Flyme,可以使用getprop命令,去系统build.prop文件查找是否有对应属性 ...
		 
						- javascript判断设备类型-手机(mobile)、安卓(android)、电脑(pc)、其他(ipad/iPod/Windows)等
		
使用device.js检测设备并实现不同设备展示不同网页 html代码: <!doctype html> <html> <head> <meta charse ...
		 
						- [转]Android WebView播放视频(包括全屏播放),androidwebview
		
Android WebView播放视频(包括全屏播放),androidwebview 最近项目开发中用到了WebView播放视频的功能,总结了开发中犯过的错误,这些错误在开发是及容易遇到的,所以我这里 ...
		 
						- android 随手记 videoview循环播放网络视频 和mediaplayer+sufaceview播放网络视频
		
1:videoview循环播放视频 1>xml <RelativeLayout xmlns:android="http://schemas.android.com/apk/res ...
		 
						- 使用Vitamio打造自己的Android万能播放器(6)——在线播放(播放列表)
		
前言 新版本的VPlayer由设计转入开发阶段,预计开发周期为一个月,这也意味着新版本的Vitamio将随之发布,开发者们可以和本系列文章一样,先开发其他功能.本章内容为"在线视频播放列表& ...
		 
						- 【Android】Android 代码判断是否获取ROOT权限(二)
		
[Android]Android 代码判断是否获取ROOT权限 方法比较简单,直接粘贴代码 /** * 判断当前手机是否有ROOT权限 * @return */ public boolean isRo ...
		 
						- js判断是否手机自动跳转移动端
		
写法一: {literal} <script> //判断是否手机自动跳转 var browser={versions:function(){var u=navigator.userAgen ...
		 
						- Android中判断网络连接是否可用及监控网络状态
		
Android中判断网络连接是否可用及监控网络状态 作者: 字体:[增加 减小] 类型:转载 获取网络信息需要在AndroidManifest.xml文件中加入相应的权限,接下来详细介绍Android ...
		 
						- ThinkPHP在入口文件中判断是手机还是PC端访问网站
		
<?php// +----------------------------------------------------------------------// | ThinkPHP [ WE ...
		 
		
	
随机推荐
	
									- bzoj2431逆序对数列
			
题目:https://www.lydsy.com/JudgeOnline/problem.php?id=2431 很容易想到n^3的做法.就是前 i 个数用第 i 个数最多能 i - 1 个逆序对,所 ...
			 
						- CentOS 6.5 下HeartBeat的安装与配置
			
CentOS 6.5 下HeartBeat的安装与配置 参考网站: http://wenku.baidu.com/link?url=BvqJatdx1m12PLil-7YA1zkM0yUOEO8OnN ...
			 
						- Apache Tomcat8必备知识(完整的支持WebSockets 1.0)
			
一.Apache Tomcat 8介绍 Apache Tomcat 8RC1版于前几日发布.它 经过了2年的开发,引入了很多新特征,由于目前还只是Alpha版,故不推荐在产品中使用.但是我们应该了解它 ...
			 
						- xhprof使用
			
一.下载安装 wget http://pecl.php.net/get/xhprof-0.9.3.tgz tar zxvf xhprof-0.9.3.tgz cd xhprof-0.9.3/exten ...
			 
						- OpenSSL生成root CA及签发证书
			
一.openssl 简介 openssl 是目前最流行的 SSL 密码库工具,其提供了一个通用.健壮.功能完备的工具套件,用以支持SSL/TLS 协议的实现.官网:https://www.openss ...
			 
						- vim自定义配置之nerdTree
			
vimConfig/plugin/nerdTree-setting.vim let g:NERDTree_title="[NERDTree]" nmap <F2> :N ...
			 
						- Windows Installer (MSI)知识学习
			
http://www.cnblogs.com/QuitGame/archive/2006/01/10/314589.html 所有的安装过的程序都在C:\Windows\Installer下有缓存
			 
						- [持续更新]一些zyys的题的集合
			
Luogu P1119 灾后重建 Sol:对于每个中转点K,需且仅需以此松弛一次 Key words:Floyd,本质活用 考题 路径数 题目描述: Euphemia到一个N*N的药草田里采药,她从左 ...
			 
						- Bootstrap-CSS
			
ylbtech-Bootstrap-Plugin: 1.返回顶部 1.   2. 2.返回顶部   3.返回顶部   4.返回顶部   5.返回顶部 1.   2.   11.返回顶部   作者:yl ...
			 
						- oracle to_date函数和mysql  DATE_FORMAT函数用法
			
一.在Oracle中,当想把字符串为‘2011-09-20 08:30:45’的格式转化为日期格式,我们可以使用oracle提供的to_date函数. sql语句为: SELECT to_date(' ...