今天在研究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, [email protected] #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源码。