android发现之旅之媒体按键(耳机按键播放暂停键等)处理过程

android对媒体按键,比如上一首,下一首,播放,暂停,快进,快退等。当然,也包括耳机上的按键,拨打电话,接听电话,挂断电话等。本篇来讲述android如何传递媒体按键的。

1. 都有哪些媒体按键(PhoneWindowManager.java)

4167             case KeyEvent.KEYCODE_HEADSETHOOK:

4168             case KeyEvent.KEYCODE_MUTE:    

4169             case KeyEvent.KEYCODE_MEDIA_STOP:

4170             case KeyEvent.KEYCODE_MEDIA_NEXT:

4171             case KeyEvent.KEYCODE_MEDIA_PREVIOUS:

4172             case KeyEvent.KEYCODE_MEDIA_REWIND:

4173             case KeyEvent.KEYCODE_MEDIA_RECORD:

4174             case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD:

4175             case KeyEvent.KEYCODE_MEDIA_AUDIO_TRACK: {

2. 按键传递的发源地

(之前的流程,参考android按键处理)

PhoneWindowManager.java里接口dispatchUnhandledKey负责处理下层报过来的按键。调用interceptFallback。interceptFallback调用interceptKeyBeforeQueueing。

interceptKeyBeforeQueueing接口根据按键类型,做不同的处理,比如著名的power键也是在这处理的。这里只关注媒体按键,处理媒体按键,发送了一个消息给自己:

4182                     mBroadcastWakeLock.acquire();

4183                     Message msg = mHandler.obtainMessage(

MSG_DISPATCH_MEDIA_KEY_WITH_WAKE_LOCK,

4184                             new KeyEvent(event));          

4185                     msg.setAsynchronous(true);     

4186                     msg.sendToTarget();    

后边用dispatchMediaKeyWithWakeLock处理这个消息,实际调用了dispatchMediaKeyWithWakeLockToAudioService接口,4309     void dispatchMediaKeyWithWakeLockToAudioService(KeyEvent event) {

4310         if (ActivityManagerNative.isSystemReady()) {

4311             IAudioService audioService = getAudioService();

4312             if (audioService != null) {

4313                 try {

4314                    

audioService.dispatchMediaKeyEventUnderWakelock(event);

4315                 } catch (RemoteException e) {

4316                     Log.e(TAG, "dispatchMediaKeyEvent threw exception " + e);

4317                 }

4318             }

调用了AudioService的dispatchMediaKeyEventUnderWakelock。

AudioService.java里dispatchMediaKeyEventUnderWakelock接口,调用了mMediaFocusControl.dispatchMediaKeyEventUnderWakelock。

MediaFocusControl.java的dispatchMediaKeyEventUnderWakelock接口调用了,filterMediaKeyEvent接口,处理媒体按键。

746     private void filterMediaKeyEvent(KeyEvent keyEvent, boolean needWakeLock) {  747         // sanity check on the incoming key event

                 先判断是不是媒体按键。

 748         if (!isValidMediaKeyEvent(keyEvent)) {

 749             Log.e(TAG, "not dispatching invalid media key event " + keyEvent);

 750             return;

 751         }

如果有telephony相关的锁,优先处理把事件给telephony来处理。

 752         // event filtering for telephony

 753         synchronized(mRingingLock) {

 754             synchronized(mRCStack) {

 755                 if ((mMediaReceiverForCalls != null) &&

 756                         (mIsRinging || (mAudioService.getMode() == AudioSystem.MODE_IN_CALL))) {

 757                     dispatchMediaKeyEventForCalls(keyEvent, needWakeLock);

 758                     return;

 759                 }

 760             }

 761         }

如果telephony没处理,先判断是不是语音按键,不是就调用dispatchMediaKeyEvent

762         // event filtering based on voice-based interactions

 763         if (isValidVoiceInputKeyCode(keyEvent.getKeyCode())) {

 764             filterVoiceInputKeyEvent(keyEvent, needWakeLock);

 765         } else {

 766             dispatchMediaKeyEvent(keyEvent, needWakeLock);

 767         }

 768     }

dispatchMediaKeyEvent接口,

先准备了一个Intent:

801     private void dispatchMediaKeyEvent(KeyEvent keyEvent, boolean needWakeLock) {

 802         if (needWakeLock) {

 803             mMediaEventWakeLock.acquire();

 804         }

 805         Intent keyIntent = new Intent(Intent. ACTION_MEDIA_BUTTON , null);

 806         keyIntent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent);

之后判断是否有专门关注媒体按键的客户端,有就调用。

807         synchronized(mRCStack) {

 808             if (!mRCStack.empty()) {

 809                 // send the intent that was registered by the client

 810                 try {

 811                     mRCStack.peek().

mMediaIntent.send(mContext,

 812                             needWakeLock ? WAKELOCK_RELEASE_ON_FINISHED : 0 /*code*/,

 813                             keyIntent, this, mEventHandler);

没有就广播了,这样大家都能收到了。

824                 final long ident = Binder.clearCallingIdentity();

 825                 try {

 826                     mContext.

sendOrderedBroadcastAsUser(keyIntent,

UserHandle.ALL,

 827                             null, mKeyEventDone,

 828                             mEventHandler, Activity.RESULT_OK, null, null);

3. 理清AudioService里的几个接口

registerRemoteControlClient 注册远程控制客户端,所谓远程控制,实际上就是寄生的界面,同时用receiver处理媒体按键的一种播放形式,比如后台的音乐。

registerMediaButtonIntent注册媒体按键,用于获取和处理媒体按键。

registerMediaButtonEventReceiverForCalls注册通话媒体按键处理。

4. 场景举例

(1) 处理耳机按键

根据实现,我们知道,有2种方法:

--1-- 关心Intent.ACTION_MEDIA_BUTTON

--2-- 其实也是关心Intent.ACTION_MEDIA_BUTTON,不过更加复杂一点,用到:

MediaButtonEventReceiver。

(2) 如何阻止music应用在耳机按键按下后自动播放音乐

我们知道,music应用启动MediaPlaybackService后注册了一个RemoteControlClient以及MediaButtonEventReceiver。这就意味着,我们如果是关心Intent.ACTION_MEDIA_BUTTON的话,是拿不到耳机按键的,会被music应用截掉。

所以我们只能也注册一个MediaButtonEventReceiver了。

你可能感兴趣的:(Android)