• Android中通过耳机按键控制音乐播放的实现


    今天在研究Android中实现Android 4.2.2源码中的Music应用的源码,关于通过耳机按键控制音乐播放的实现,有点好奇,就仔细分析了一下源码,

    主要由 MediaButtonIntentReceiver 这个类来实现。


    在AndroidManifest.xml中有如下Receiver的注册:

            <receiver android:name="com.huawei.imax.music.MediaButtonIntentReceiver" >
                <intent-filter>
                    <action android:name="android.intent.action.MEDIA_BUTTON" />
                    <action android:name="android.media.AUDIO_BECOMING_NOISY" />
                </intent-filter>
            </receiver>


    其实关键是对这两个ACTION的监控:

      android.intent.action.MEDIA_BUTTON的说明如下,从注释看,就是媒体按键被按下后,通过 Intent.EXTRA_KEY_EVENT 中带上触发此事件的具体按钮事件

       /**
         * Broadcast Action:  The "Media Button" was pressed.  Includes a single
         * extra field, {@link #EXTRA_KEY_EVENT}, containing the key event that
         * caused the broadcast.
         */
        @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
        public static final String ACTION_MEDIA_BUTTON = "android.intent.action.MEDIA_BUTTON";


      android.intent.action.AUDIO_BECOMING_NOISY的说明如下(自己翻译的,大概这个意思),

    从注释看,当音频变得“吵闹”,比如耳机拔出,或者A2DP音频通道(比如通过蓝牙音箱播放音乐就是一种A2DP的应用场景)断开,

    音频系统将会自动将音频转到自带的

        /**
         * Broadcast intent, a hint for applications that audio is about to become
         * 'noisy' due to a change in audio outputs. For example, this intent may
         * be sent when a wired headset is unplugged, or when an A2DP audio
         * sink is disconnected, and the audio system is about to automatically
         * switch audio route to the speaker. Applications that are controlling
         * audio streams may consider pausing, reducing volume or some other action
         * on receipt of this intent so as not to surprise the user with audio
         * from the speaker.
         */
        @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
        public static final String ACTION_AUDIO_BECOMING_NOISY = "android.media.AUDIO_BECOMING_NOISY";


    MediaButtonIntentReceiver 的关键实现如下:

    public class MediaButtonIntentReceiver extends BroadcastReceiver
    {
        ...
        
        @Override
        public void onReceive(Context context, Intent intent)
        {
            String intentAction = intent.getAction();
            if (AudioManager.ACTION_AUDIO_BECOMING_NOISY.equals(intentAction))
            {
                Intent i = new Intent(context, MediaPlaybackService.class);
                i.setAction(MediaPlaybackService.SERVICECMD);
                i.putExtra(MediaPlaybackService.CMDNAME,
                        MediaPlaybackService.CMDPAUSE);
                context.startService(i);
            }
            else if (Intent.ACTION_MEDIA_BUTTON.equals(intentAction))
            {
                KeyEvent event = (KeyEvent) intent.getParcelableExtra(Intent.EXTRA_KEY_EVENT);
                
                if (event == null)
                {
                    return;
                }
                
                int keycode = event.getKeyCode();
                int action = event.getAction();
                long eventtime = event.getEventTime();
                
                // single quick press: pause/resume. 
                // double press: next track
                // long press: start auto-shuffle mode.
                
                String command = null;
                switch (keycode)
                {
                    case KeyEvent.KEYCODE_MEDIA_STOP:
                        command = MediaPlaybackService.CMDSTOP;
                        break;
                    case KeyEvent.KEYCODE_HEADSETHOOK:
                    case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
                        command = MediaPlaybackService.CMDTOGGLEPAUSE;
                        break;
                    case KeyEvent.KEYCODE_MEDIA_NEXT:
                        command = MediaPlaybackService.CMDNEXT;
                        break;
                    case KeyEvent.KEYCODE_MEDIA_PREVIOUS:
                        command = MediaPlaybackService.CMDPREVIOUS;
                        break;
                    case KeyEvent.KEYCODE_MEDIA_PAUSE:
                        command = MediaPlaybackService.CMDPAUSE;
                        break;
                    case KeyEvent.KEYCODE_MEDIA_PLAY:
                        command = MediaPlaybackService.CMDPLAY;
                        break;
                }


    通过接收此事件的广播,想进行什么操作,完全看你的业务需求了。


    一开始以为耳机插拔,是通过ACTION_HEADSET_PLUG这个Action来控制,但Music并没有监听此事件,

        /**
         * Broadcast Action: Wired Headset plugged in or unplugged.
         *
         * <p>The intent will have the following extra values:
         * <ul>
         *   <li><em>state</em> - 0 for unplugged, 1 for plugged. </li>
         *   <li><em>name</em> - Headset type, human readable string </li>
         *   <li><em>microphone</em> - 1 if headset has a microphone, 0 otherwise </li>
         * </ul>
         * </ul>
         */
        @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
        public static final String ACTION_HEADSET_PLUG =
                "android.intent.action.HEADSET_PLUG";

    自己测试了一下,在AndroidManifest.xml中静态注册,是不会生效的,为啥?

    一个很简单的解释,如果你的应用还没有运行,这时插入耳机,系统如何把这个消息给这个应用?

    因此,"android.intent.action.HEADSET_PLUG" 只能通过动态注册来接收此广播消息。


    如果让 MediaButtonIntentReceiver 还接收"android.intent.action.HEADSET_PLUG"的广播消息,

    则MediaButtonIntentReceiver会先收到 "android.media.AUDIO_BECOMING_NOISY" 这个消息,然后才会收到"android.intent.action.HEADSET_PLUG"这个消息?

    为什么这样,代码层面还没有分析,后面抽空再研究下Android源码。


  • 相关阅读:
    清除浮动的四种方式
    简单的响应式-信息介绍
    Island Mentality
    六、排序-插入排序
    五、快速排序
    有关一些话
    有关学习态度
    无题
    四、链表的学习
    MySQ彻底删除与安装配置
  • 原文地址:https://www.cnblogs.com/pangblog/p/3315550.html
Copyright © 2020-2023  润新知