转自:http://blog.csdn.net/qinjuning/article/details/6938436
在Android中并没有定义MediaButtonReceive这个广播类,MediaButtonReceive只是作为一种通俗的命名方式来响应
插入耳机后,点击耳机上的按钮(名称:MEDIA_BUTTON)接受该广播事件的类。所有该MEDIA_BUTTON的按下我们就简称
为MEDIA_BUTTON广播吧。
顾名思义:它显然是一个广播接收器类(BroadbcastReceiver),那么它就具备了BroadbcastReceiver类的使用方式,
但是,因为它需要通过AudioManager对象注册,所以它有着自己的独特之处(否则我也不会单独拿出来分析,- -),后面我们
会慢慢的讲解。
点击MEDIA_BUTTON发送的Intent Action 为:
ACTION_MEDIA_BUTTON ="android.intent.action.MEDIA_BUTTON"
Intent 附加值为(Extra)点击MEDIA_BUTTON的按键码 :
//获得KeyEvent对象
KeyEvent keyEvent = (KeyEvent)intent.getParcelableExtra(Intent.EXTRA_KEY_EVENT);
//获得Action
String intentAction = intent.getAction() ;
AudioManager对象注册MEDIA_BUTTON广播的方法原型为:
public voidregisterMediaButtonEventReceiver(ComponentNameeventReceiver)
Register a component to be the sole receiverof MEDIA_BUTTON intents
Parameters:
identifier of a BroadcastReceiver that will receive the media button intent. This broadcast receiver
must be declared in the application manifest.
从注释可知以下两点:
1、 在AudioManager对象注册一个MediaoButtonRecevie,使它成为MEDIA_BUTTON的唯一接收器(这很重要,
我们会放在后面讲解) 也就是说只有我能收到,其他的都收不到这个广播了,否则的话大家都收到会照成一定的混乱;
2、 该广播必须在AndroidManifest.xml文件中进行声明,否则就监听不到该MEDIA_BUTTON广播了。
下面我们就简单的写一个MediaButtonReceiver类,并且在AndroidManifest.xml定义
1、 自定义的MediaButtonReceiver 广播类
- 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 MediaButtonReceiver extends BroadcastReceiver {
- private static String TAG = "MediaButtonReceiver";
- @Override
- public void onReceive(Context context, Intent intent) {
-
- String intentAction = intent.getAction();
-
- 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();
-
-
- StringBuilder sb = new StringBuilder();
-
- if (KeyEvent.KEYCODE_MEDIA_NEXT == keyCode) {
- sb.append("KEYCODE_MEDIA_NEXT");
- }
-
-
- 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());
- }
- }
- }
2、 在AndroidManifest.xml声明我们定义的广播类。
- "MediaButtonReceiver">
-
- "android.intent.action.MEDIA_BUTTON">
-
-
在模拟器上,我们可以手动构造MEDA_BUTTON的广播,并且将它发送出去(后面会介绍)。
如果有真机测试的话,按下MEDIA_BUTTON是可以接受到MEDIA_BUTTON广播的,如果没有接受到,请关闭所有应用
程序,在观察效果。
继续我们的下一步分析:
前面我们说明通过registerMediaButtonEventReceiver(eventReceiver)方法注册时,使它成为MEDIA_BUTTON的
唯一 接收器。这个唯一是怎么实现的呢? 我们在源码中,一步步追本溯源,相信一定可以找到答案,知道这“唯一“是
怎么来的。
第一步、 为AudioManager注册一个MediaButtonReceiver() ;
-
- AudioManager mAudioManager =(AudioManager)getSystemService(Context.AUDIO_SERVICE);
-
-
- ComponentName mbCN = new ComponentName(getPackageName(),MediaButtonReceiver.class.getName());
-
- mAudioManager.registerMediaButtonEventReceiver(mbCN);
-
- mAudioManager.unregisterMediaButtonEventReceiver(mbCN);
MediaButtonReceiver就是我们用来接收MEDIA_BUTTON的广播类,下面为了叙述方便和直观上得体验,我直接使用
ComponentName类来替代真正的MediaoButtonReceiver广播类。
说明 接下来分析的文件路径全部在 frameworks/base/media/java/android/media/ 下
第二步、 进入AudioManager.java进行查看 ,发现如下方法:
-
- public void registerMediaButtonEventReceiver(ComponentName eventReceiver) {
-
-
- IAudioService service = getService();
- try {
-
- service.registerMediaButtonEventReceiver(eventReceiver);
-
- } catch (RemoteException e) {
- Log.e(TAG, "Dead object in registerMediaButtonEventReceiver"+e);
- }
- }
-
- public void unregisterMediaButtonEventReceiver(ComponentName eventReceiver) {
- IAudioService service = getService();
- try {
-
- service.unregisterMediaButtonEventReceiver(eventReceiver);
- } catch (RemoteException e) {
- Log.e(TAG, "Dead object in unregisterMediaButtonEventReceiver"+e);
- }
- }
找到getService()方法,其实现为:
-
- private static IAudioService getService()
- {
-
- if (sService != null) {
- return sService;
- }
-
-
- IBinder b = ServiceManager.getService(Context.AUDIO_SERVICE);
-
- sService = IAudioService.Stub.asInterface(b);
- return sService;
- }
-
- private static IAudioService sService;
我们知道了AudiaoManager只不过是一个傀儡,所有的方法都是由IAudioService 对象去实现的,通过它的构造方式,
可以知道它应该是有AIDL文件形成的Binder机制, sService只是客户端对象,那么它的服务端对象在什么地方呢?
也就是继承了IAudioService.Stub桩的类。
第三步、接下来我们需要找到该IAudioService.aidl文件和真正的服务端对象
IAudioService.aidl定义如下:
- package android.media;
-
- import android.content.ComponentName;
- import android.media.IAudioFocusDispatcher;
-
-
-
- interface IAudioService {
-
- void adjustVolume(int direction, int flags);
- void adjustSuggestedStreamVolume(int direction, int suggestedStreamType, int flags);
- void adjustStreamVolume(int streamType, int direction, int flags);
- void setStreamVolume(int streamType, int index, int flags);
- void setStreamSolo(int streamType, boolean state, IBinder cb);
- void setStreamMute(int streamType, boolean state, IBinder cb);
- int getStreamVolume(int streamType);
- int getStreamMaxVolume(int streamType);
- void setRingerMode(int ringerMode);
- int getRingerMode();
- void setVibrateSetting(int vibrateType, int vibrateSetting);
- int getVibrateSetting(int vibrateType);
- boolean shouldVibrate(int vibrateType);
- void setMode(int mode, IBinder cb);
- int getMode();
- oneway void playSoundEffect(int effectType);
- oneway void playSoundEffectVolume(int effectType, float volume);
- boolean loadSoundEffects();
- oneway void unloadSoundEffects();
- oneway void reloadAudioSettings();
- void setSpeakerphoneOn(boolean on);
- boolean isSpeakerphoneOn();
- void setBluetoothScoOn(boolean on);
- boolean isBluetoothScoOn();
- int requestAudioFocus(int mainStreamType, int durationHint, IBinder cb, IAudioFocusDispatcher l, String clientId);
- int abandonAudioFocus(IAudioFocusDispatcher l, String clientId);
- void unregisterAudioFocusClient(String clientId);
- void registerMediaButtonEventReceiver(in ComponentName eventReceiver);
- void unregisterMediaButtonEventReceiver(in ComponentName eventReceiver);
- void startBluetoothSco(IBinder cb);
- void stopBluetoothSco(IBinder cb);
- }
真正的服务端对象就是继承了 IAudioService.Stub 桩的类,AudioService就是该服务端对象,其实AudioManager的
所有操作都是由AudioService来实现的,它才是真正的老大。
第五步、 AudioService.java
-
- public class AudioService extends IAudioService.Stub {
-
-
-
- public void registerMediaButtonEventReceiver(ComponentName eventReceiver) {
- Log.i(TAG, " Remote Control registerMediaButtonEventReceiver() for " + eventReceiver);
-
- synchronized(mRCStack) {
-
- pushMediaButtonReceiver(eventReceiver);
- }
- }
-
-
-
- private static class RemoteControlStackEntry {
- public ComponentName mReceiverComponent;
-
-
-
- public RemoteControlStackEntry() {
- }
-
- public RemoteControlStackEntry(ComponentName r) {
- mReceiverComponent = r;
- }
- }
-
-
- private Stack mRCStack = new Stack();
-
-
- private void pushMediaButtonReceiver(ComponentName newReceiver) {
-
-
-
- if (!mRCStack.empty() && mRCStack.peek().mReceiverComponent.equals(newReceiver)) {
- return;
- }
-
- Iterator stackIterator = mRCStack.iterator();
-
- while(stackIterator.hasNext()) {
- RemoteControlStackEntry rcse = (RemoteControlStackEntry)stackIterator.next();
-
- if(rcse.mReceiverComponent.equals(newReceiver)) {
- mRCStack.remove(rcse);
- break;
- }
- }
-
- mRCStack.push(new RemoteControlStackEntry(newReceiver));
- }
- }
小结一下:
栈(mRCStack)维护了所有CompoentName对象,对每个CompoentName对象,保证它有且仅有一个,
新注册的CompoentName对象永远处于栈顶
我们看下取消注册的方法:
-
-
- public void unregisterMediaButtonEventReceiver(ComponentName eventReceiver) {
- Log.i(TAG, " Remote Control unregisterMediaButtonEventReceiver() for " + eventReceiver);
-
- synchronized(mRCStack) {
-
- removeMediaButtonReceiver(eventReceiver);
- }
- }
-
- private void removeMediaButtonReceiver(ComponentName newReceiver) {
- Iterator stackIterator = mRCStack.iterator();
- while(stackIterator.hasNext()) {
-
- RemoteControlStackEntry rcse = (RemoteControlStackEntry)stackIterator.next();
-
- if(rcse.mReceiverComponent.equals(newReceiver)) {
- mRCStack.remove(rcse);
- break;
- }
- }
- }
通过对前面的学习,我们知道了AudioManager内部利用一个栈来管理包括加入和移除ComponentName对象,
新的疑问来了?这个MEDIA_BUTTON广播是如何分发的呢 ?
其实,AudioService.java文件中也存在这么一个MediaoButtonReceiver的广播类,它为系统广播接收器,即用来接收
系统的MEDIA_BUTTON广播,当它接收到了这个MEDIA_BUTTON广播 ,它会对这个广播进行进一步处理,这个处理过程
就是我们需要的弄清楚。
MediaButtonBroadcastReceiver 内部类如下:
- private class MediaButtonBroadcastReceiver extends BroadcastReceiver {
- @Override
- public void onReceive(Context context, Intent intent) {
-
- String action = intent.getAction();
-
- if (!Intent.ACTION_MEDIA_BUTTON.equals(action)) {
- return;
- }
-
- KeyEvent event = (KeyEvent) intent.getParcelableExtra(Intent.EXTRA_KEY_EVENT);
- if (event != null) {
-
-
-
-
- if ((getMode() == AudioSystem.MODE_IN_CALL) ||(getMode() == AudioSystem.MODE_RINGTONE)) {
- return;
- }
- synchronized(mRCStack) {
-
- if (!mRCStack.empty()) {
-
-
- Intent targetedIntent = new Intent(Intent.ACTION_MEDIA_BUTTON);
- targetedIntent.putExtras(intent.getExtras());
-
- targetedIntent.setComponent(mRCStack.peek().mReceiverComponent);
-
-
- abortBroadcast();
-
-
- context.sendBroadcast(targetedIntent, null);
- }
-
-
- }
- }
- }
- }
总结一下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) {
-
- String intentAction = intent.getAction() ;
-
- 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();
-
-
- StringBuilder sb = new StringBuilder();
-
- if(KeyEvent.KEYCODE_MEDIA_NEXT == keyCode){
- sb.append("KEYCODE_MEDIA_NEXT");
- }
-
- 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());
-
- }
-
- }
-
- }
2 、England_MBReceiver.java同于China_MBRreceiver ,打印Log TAG= "England_MBReceiver"
3、在AndroidManifest.xml文件定义:
- ".China_MBReceiver">
-
- "android.intent.action.MEDIA_BUTTON">
-
-
-
- ".Enaland_MBReceiver">
-
- "android.intent.action.MEDIA_BUTTON">
-
-
4、MainActivity .java 我们通过手动构造一个MEDIA_BUTTON广播去查看我们的MediaButtonReceiver类的打印信息。
- package com.qin.mediabutton;
-
- import android.app.Activity;
- import android.content.ComponentName;
- import android.content.Context;
- import android.content.Intent;
- import android.media.AudioManager;
- import android.os.Bundle;
- import android.view.KeyEvent;
-
- public class MainActivity extends Activity {
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.main);
-
-
- Intent mbIntent = new Intent(Intent.ACTION_MEDIA_BUTTON);
-
- KeyEvent keyEvent = new KeyEvent (KeyEvent.ACTION_DOWN,KeyEvent.KEYCODE_HEADSETHOOK) ;
-
- mbIntent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent);
-
-
- sendBroadcast(mbIntent);
-
-
- AudioManager mAudioManager = (AudioManager)getSystemService(Context.AUDIO_SERVICE);
-
- ComponentName chinaCN = new ComponentName(getPackageName(),China_MBReceiver.class.getName());
-
-
- mAudioManager.registerMediaButtonEventReceiver(chinaCN);
-
- }
-
- protected void onDestroy(){
- super.onDestroy() ;
- AudioManager mAudioManager = (AudioManager)getSystemService(Context.AUDIO_SERVICE);
- ComponentName chinaCN = new ComponentName(getPackageName(),China_MBReceiver.class.getName());
-
- mAudioManager.unregisterMediaButtonEventReceiver(chinaCN);
- }
- }
值得注意的一点时,当我们为一个应用程序注册了MediaoButtonReceiver时,在程序离开时,我们需要取消该
MediaoButtonReceiver的注册,在onDestroy()调用unregisterMediaButtonEventReceiver()方法就OK,这样应用程序之间
的交互就更具逻辑性了。