Platform: Rockchip
OS: Android 6.0
Kernel: 3.10.92
在看music后台自动播放的问题时因为对这块不了解,以为开机默认打印的这条log会有关系
ActivityManager: Start proc 978:com.android.music/u0a29 for broadcast com.android.music/.MediaButtonIntentReceiver
事实上,这是和MediaButton有关系。
通俗一点讲,就是在你插了耳机,如果有些上面有按键,当你按下之后,系统就会调用music来播放音乐,此功能正是通过MediaButtonIntentReceiver来接收然后处理播放/暂停/下一曲等功能的。
本质上它也是一个Broadcast Receiver,在xml有静态定义:
packages/apps/Music/AndroidManifest.xml
<receiver android:name="com.android.music.MediaButtonIntentReceiver">
<intent-filter>
<action android:name="android.intent.action.MEDIA_BUTTON" />
<action android:name="android.media.AUDIO_BECOMING_NOISY" />
intent-filter>
receiver>
接收”android.intent.action.MEDIA_BUTTON” 和 “android.media.AUDIO_BECOMING_NOISY”两种Action.
前者处理按键事件, 后者处理比如耳机插拔等事件(比如收到事件后停止播放)。具体说明如下:
Intent.java
/**
* 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";
AudioManager.java
/**
* 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";
packages/apps/Music/src/com/android/music/MediaPlaybackService.java
public void onCreate() {
//MediaButton类的注册都要通过AudioManager实现和管理
mAudioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
ComponentName rec = new ComponentName(getPackageName(),
MediaButtonIntentReceiver.class.getName());
//注册接口
mAudioManager.registerMediaButtonEventReceiver(rec);
}
packages/apps/Music/src/com/android/music/MediaButtonIntentReceiver.java
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)) {
......
if (command != null) {
//按下处理
if (action == KeyEvent.ACTION_DOWN) {
if (mDown) {
if ((MediaPlaybackService.CMDTOGGLEPAUSE.equals(command) ||
MediaPlaybackService.CMDPLAY.equals(command))
&& mLastClickTime != 0
&& eventtime - mLastClickTime > LONG_PRESS_DELAY) {
mHandler.sendMessage(
mHandler.obtainMessage(MSG_LONGPRESS_TIMEOUT, context));
}
} else if (event.getRepeatCount() == 0) {
......
}
if (isOrderedBroadcast()) {
abortBroadcast();
}
}
}
}
}
每个应用程序都可以注册MediaButton这种Receiver,比如当前有酷狗和系统自带音乐播放器,当按键按下的时候应该调用哪个来播放呢?
老版本的处理:
系统的处理行为是: 哪个应用注册得晚,就使用哪个应用来播放。
原理是: 系统通过一个堆栈来保存当前所有注册的MediaButttonReceiver, 按照先进后出的原则,最后一个注册的Receviver将会被放在栈顶,
如果已经注册过了,那么先删除,再放在栈顶。
上面这部分功能是在AudioService中实现的。
新版本处理:
用MediaSession进行保存,当有按键事件发生时,通过判断当前应用是否正在播放以及优先级来决定发送给哪个应用程序。
注册过程:
registerMediaButtonEventReceiver -> registerMediaButtonIntent -> helper.addMediaButtonListener ->
MediaButtonListener -> holder.mSession.setMediaButtonReceiver
系统收到事件后的处理:
sendMediaButtonEvent -> mSessionManager.dispatchMediaKeyEvent -> dispatchMediaKeyEvent(MediaSessionManager.java)
Android中通过耳机按键控制音乐播放的实现
audio系列之MediaButton–基于Android7.0
Android中MediaButtonReceiver广播监听器的机制分析
Add Headset button support to your Android application
Android耳机Hook键功能