通过对前面的学习,我们知道了AudioManager内部利用一个栈来管理包括加入和移除ComponentName对象,
新的疑问来了?这个MEDIA_BUTTON广播是如何分发的呢 ?
其实,AudioService.java文件中也存在这么一个MediaoButtonReceiver的广播类,它为系统广播接收器,即用来接收
系统的MEDIA_BUTTON广播,当它接收到了这个MEDIA_BUTTON广播 ,它会对这个广播进行进一步处理,这个处理过程
就是我们需要的弄清楚。
MediaButtonBroadcastReceiver 内部类如下:
[java]
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
[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()); } } }