我想实现如下的场景,判断当前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如何判断当前手机是否正在播放音乐,并获取到正在播放的音乐的信息的更多相关文章

  1. Android: 判断当前手机品牌(转)

    参考资料 Android判断手机ROM 正文 有时候需要判断手机系统的ROM,检测ROM是MIUI.EMUI还是Flyme,可以使用getprop命令,去系统build.prop文件查找是否有对应属性 ...

  2. javascript判断设备类型-手机(mobile)、安卓(android)、电脑(pc)、其他(ipad/iPod/Windows)等

    使用device.js检测设备并实现不同设备展示不同网页 html代码: <!doctype html> <html> <head> <meta charse ...

  3. [转]Android WebView播放视频(包括全屏播放),androidwebview

    Android WebView播放视频(包括全屏播放),androidwebview 最近项目开发中用到了WebView播放视频的功能,总结了开发中犯过的错误,这些错误在开发是及容易遇到的,所以我这里 ...

  4. android 随手记 videoview循环播放网络视频 和mediaplayer+sufaceview播放网络视频

    1:videoview循环播放视频 1>xml <RelativeLayout xmlns:android="http://schemas.android.com/apk/res ...

  5. 使用Vitamio打造自己的Android万能播放器(6)——在线播放(播放列表)

    前言 新版本的VPlayer由设计转入开发阶段,预计开发周期为一个月,这也意味着新版本的Vitamio将随之发布,开发者们可以和本系列文章一样,先开发其他功能.本章内容为"在线视频播放列表& ...

  6. 【Android】Android 代码判断是否获取ROOT权限(二)

    [Android]Android 代码判断是否获取ROOT权限 方法比较简单,直接粘贴代码 /** * 判断当前手机是否有ROOT权限 * @return */ public boolean isRo ...

  7. js判断是否手机自动跳转移动端

    写法一: {literal} <script> //判断是否手机自动跳转 var browser={versions:function(){var u=navigator.userAgen ...

  8. Android中判断网络连接是否可用及监控网络状态

    Android中判断网络连接是否可用及监控网络状态 作者: 字体:[增加 减小] 类型:转载 获取网络信息需要在AndroidManifest.xml文件中加入相应的权限,接下来详细介绍Android ...

  9. ThinkPHP在入口文件中判断是手机还是PC端访问网站

    <?php// +----------------------------------------------------------------------// | ThinkPHP [ WE ...

随机推荐

  1. replace U to T in mature.fa

    sed '2~2s/U/T/g' mature.fa > miRBase_mature.fa

  2. JavaScript:单选钮的事件处理

    单选按钮事件: 单选钮属于多选一的处理流程,但是单选钮由于也是HTML元素,所以对于JavaScript而言也表示对象. 注意:单选钮的特点是一定要需要名相同才可以实现,所以此处如果名字相同,那么就是 ...

  3. 二模 (4)day2

    第一题: 题目大意:给出1~n的一个排列,统计该排列有多少个长度为奇数的连续子序列的中位数是b.中位数是指把所有元素从小到大排列后,位于中间的数. N<=100000 1.考虑到一个符合要求的连 ...

  4. Windows2003屏蔽IP

    1.打开本地安全策略   2.创建新的IP策略   去掉勾选向导  我们编辑 直接右键指派   指派可以看出来生效...网络已经不通了              

  5. request 和response 中的setCharacterEncoding区别

    response和request的setCharacterEncoding 一.request.setCharacterEncoding():是设置从request中取得的值或从数据库中取出的值. 指 ...

  6. 接收POst数据流数据

    var data = "";                using (StreamReader readStream = new StreamReader(context.Re ...

  7. [设计模式]&lt;&lt;设计模式之禅&gt;&gt;关于里氏替换原则

    在面向对象的语言中,继承是必不可少的.非常优秀的语言机制,它有如下优点:● 代码共享,减少创建类的工作量,每个子类都拥有父类的方法和属性:● 提高代码的重用性:● 子类可以形似父类,但又异于父类,“龙 ...

  8. LeetCode_Triangle

    Given a triangle, find the minimum path sum from top to bottom. Each step you may move to adjacent n ...

  9. sudo :apt-get:command not found

    在centos下用yum install xxx yum和apt-get的区别 一般来说著名的linux系统基本上分两大类:  1.RedHat系列:Redhat.Centos.Fedora等  2. ...

  10. 初学者易上手的SSH-hibernate04 一对一 一对多 多对多

    这章我们就来学习下hibernate的关系关联,即一对一(one-to-one),一对多(one-to-many),多对多(many-to-many).这章也将是hibernate的最后一章了,用于初 ...