其实,AudioService.java文件中也存在这么一个MediaoButtonReceiver的广播类,它为系统广播接收器,即用来接收系统的MEDIA_BUTTON广播,当它接收到了这个MEDIA_BUTTON广播 ,它会对这个广播进行进一步处理,这个处理过程就是我们需要的弄清楚。
MediaButtonBroadcastReceiver 内部类如下:
private class MediaButtonBroadcastReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
//获得action ,系统MEDIA_BUTTON广播来了
String action = intent.getAction();
//action不正确 直接返回
if (!Intent.ACTION_MEDIA_BUTTON.equals(action)) {
return;
}
//获得KeyEvent对象
KeyEvent event = (KeyEvent) intent.getParcelableExtra(Intent.EXTRA_KEY_EVENT);
if (event != null) {
// if in a call or ringing, do not break the current phone app behavior
// TODO modify this to let the phone app specifically get the RC focus
// add modify the phone app to take advantage of the new API
//来电或通话中,不做处理直接返回
if ((getMode() == AudioSystem.MODE_IN_CALL) ||(getMode() == AudioSystem.MODE_RINGTONE)) {
return;
}
synchronized(mRCStack) {
//栈不为空
if (!mRCStack.empty()) {
// create a new intent specifically aimed at the current registered listener
//构造一个Intent对象 ,并且赋予Action和KeyEvent
Intent targetedIntent = new Intent(Intent.ACTION_MEDIA_BUTTON);
targetedIntent.putExtras(intent.getExtras());
//指定该处理Intent的对象为栈顶ComponentName对象的广播类
targetedIntent.setComponent(mRCStack.peek().mReceiverComponent);
// trap the current broadcast
// 终止系统广播
abortBroadcast();
//Log.v(TAG, " Sending intent" + targetedIntent);
//手动发送该广播至目标对象去处理,该广播不再是系统发送的了
context.sendBroadcast(targetedIntent, null);
}
//假设栈为空,那么所有定义在AndroidManifest.xml的监听MEDIA_BUTTON的广播都会处理,
//在此过程中如果有任何应用程注册了registerMediaButton 该广播也会立即终止
}
}
}
}
总结一下MEDIA_BUTTON广播:
AudioManager也就是AudioService服务端对象内部会利用一个栈来管理所有ComponentName对象,所有对象有且仅有一个,
新注册的ComponentName总是会位于栈顶。
当系统发送MEDIA_BUTTON,系统MediaButtonBroadcastReceiver 监听到系统广播,它会做如下处理:
1、 如果栈为空,则所有注册了该Action的广播都会接受到,因为它是由系统发送的。
2、 如果栈不为空,那么只有栈顶的那个广播能接受到MEDIA_BUTTON的广播,手动发送了MEDIA_BUTTON
广播,并且指定了目标对象(栈顶对象)去处理该MEDIA_BUTTON 。
下面分析一下KeyEvent对象里的KeyCode按键,可能的按键码有:
1、KeyEvent.KEYCODE_MEDIA_NEXT
2、KeyEvent.KEYCODE_HEADSETHOOK
3、KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE(已废除,等同于KEYCODE_HEADSETHOOK)
4、KeyEvent.KEYCODE_MEDIA_PREVIOUS
5、KeyEvent.KEYCODE_MEDIA_STOP
PS : 在我的真机测试中,按下MEDIA_BUTTON只有KEYCODE_HEADSETHOOK可以打印出来了。
下面给出一个小DEMO检验一下我们之前所做的一切,看看MEDIA_BUTTON是如何处理分发广播的。
编写两个MediaButtonReceiver类用来监听MEDIA_BUTTON广播:
1 、China_MBReceiver.java
package com.qin.mediabutton;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.util.Log;
import android.view.KeyEvent;
public class China_MBReceiver extends BroadcastReceiver {
private static String TAG = "China_MBReceiver" ;
@Override
public void onReceive(Context context, Intent intent) {
//获得Action
String intentAction = intent.getAction() ;
//获得KeyEvent对象
KeyEvent keyEvent = (KeyEvent)intent.getParcelableExtra(Intent.EXTRA_KEY_EVENT);
Log.i(TAG, "Action ---->"+intentAction + " KeyEvent----->"+keyEvent.toString());
if(Intent.ACTION_MEDIA_BUTTON.equals(intentAction)){
//获得按键字节码
int keyCode = keyEvent.getKeyCode() ;
//按下 / 松开 按钮
int keyAction = keyEvent.getAction() ;
//获得事件的时间
long downtime = keyEvent.getEventTime();
//获取按键码 keyCode
StringBuilder sb = new StringBuilder();
//这些都是可能的按键码 , 打印出来用户按下的键
if(KeyEvent.KEYCODE_MEDIA_NEXT == keyCode){
sb.append("KEYCODE_MEDIA_NEXT");
}
//说明:当我们按下MEDIA_BUTTON中间按钮时,实际出发的是 KEYCODE_HEADSETHOOK 而不是 KEYCODE_MEDIA_PLAY_PAUSE
if(KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE ==keyCode){
sb.append("KEYCODE_MEDIA_PLAY_PAUSE");
}
if(KeyEvent.KEYCODE_HEADSETHOOK == keyCode){
sb.append("KEYCODE_HEADSETHOOK");
}
if(KeyEvent.KEYCODE_MEDIA_PREVIOUS ==keyCode){
sb.append("KEYCODE_MEDIA_PREVIOUS");
}
if(KeyEvent.KEYCODE_MEDIA_STOP ==keyCode){
sb.append("KEYCODE_MEDIA_STOP");
}
//输出点击的按键码
Log.i(TAG, sb.toString());
}
}
}