zoukankan      html  css  js  c++  java
  • 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源码。


  • 相关阅读:
    PyQt作品 – PingTester – 多点Ping测试工具
    关于和技术人员交流的一二三
    Pyjamas Python Javascript Compiler, Desktop Widget Set and RIA Web Framework
    Hybrid Qt applications with PySide and Django
    pyjamas build AJAX apps in Python (like Google did for Java)
    PyQt 维基百科,自由的百科全书
    InfoQ:请问为什么仍要选择Java来处理后端的工作?
    Eric+PyQt打造完美的Python集成开发环境
    python select module select method introduce
    GUI Programming with Python: QT Edition
  • 原文地址:https://www.cnblogs.com/pangblog/p/3315550.html
Copyright © 2011-2022 走看看