耽搁了几天,最近一直在忙找工作的事情,今天把这篇文章补上。
本文基于Android7.1.1版本进行分析,主要涉及以下几个文件:
1 AudioManager –> /frameworks/base/media/java/android/media/
2 AudioService –> /frameworks/base/services/core/java/com/android/server/audio/
3 MediaFocusControl –>/frameworks/base/services/core/java/com/android/server/audio/
4 FocusRequester –> /frameworks/base/services/core/java/com/android/server/audio/
5 AudioAttributes –> /frameworks/base/media/java/android/media/
6 AudioFocusInfo –> /frameworks/base/media/java/android/media/
7 AudioSystem –> /frameworks/base/media/java/android/media/
之前一直用的是4.4的源码,这两天看了下7.1的源码发现这块内容改动还是挺大的,主要是新增了几个文件,并且对MediaFocusControl类进行了瘦身,代码从2700多行减到了500多行。
我们从入口方法requestAudioFocus开始,还记得我们是怎么使用该方法的么?
通过Audio Manager的对象来调用
mAudioManager.requestAudioFocus(mAudioFocusChange, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN)
具体请参考我的上篇博客点击这里
从AudioManager开始(有些方法里代码较多,只贴出来部分关键代码,下同)
public int requestAudioFocus(OnAudioFocusChangeListener l, int streamType, int durationHint) { int status = AUDIOFOCUS_REQUEST_FAILED; try { status = requestAudioFocus(l, new AudioAttributes.Builder() .setInternalLegacyStreamType(streamType).build(), durationHint, 0 ); } catch (IllegalArgumentException e) { Log.e(TAG, "Audio focus request denied due to ", e); } return status;}
根据传进来的streamType,构造了一个AudioAttributes对象向下传递,这个AudioAttributes主要是存储了一些音频流信息的属性,后面会用到。接着看
@SystemApipublic int requestAudioFocus(OnAudioFocusChangeListener l, @NonNull AudioAttributes requestAttributes, int durationHint, int flags) throws IllegalArgumentException { return requestAudioFocus(l, requestAttributes, durationHint, flags & AUDIOFOCUS_FLAGS_APPS, null );}
这里面对falgs进行了与操作,由于之前传进来的是0,所以转换后的结果还是0。接着看
@SystemApipublic int requestAudioFocus(OnAudioFocusChangeListener l, @NonNull AudioAttributes requestAttributes, int durationHint, int flags, AudioPolicy ap) throws IllegalArgumentException { int status = AUDIOFOCUS_REQUEST_FAILED; registerAudioFocusListener(l); IAudioService service = getService(); try { status = service.requestAudioFocus(requestAttributes, durationHint, mICallBack, mAudioFocusDispatcher, getIdForAudioFocusListener(l), getContext().getOpPackageName() , flags, ap != null ? ap.cb() : null); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } return status;}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
这里面重点看一下 registerAudioFocusListener(l);
private final HashMap<String, OnAudioFocusChangeListener> mAudioFocusIdListenerMap = new HashMap<String, OnAudioFocusChangeListener>();public void registerAudioFocusListener(OnAudioFocusChangeListener l) { synchronized (mFocusListenerLock) { if (mAudioFocusIdListenerMap.containsKey(getIdForAudioFocusListener(l))) { return; } mAudioFocusIdListenerMap.put(getIdForAudioFocusListener(l), l); }}private String getIdForAudioFocusListener(OnAudioFocusChangeListener l) { if (l == null) { return new String(this.toString()); } else { return new String(this.toString() + l.toString()); }}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
这段代码比较好理解了,我们根据“l”来生成一个key,存储在了mAudioFocusIdListenerMap对象中,而值就是OnAudioFocusChangeListener的对象。这里用了HashMap以保证key的唯一性。至于这个map有什么用呢,先不要着急,在后面会用到的。
我们在调用AudioService的requestAudioFocus时传入了一个 mAudioFocusDispatcher参数,这个又有什么用呢?先不要着急等后面用到的时候再来看。
好了,我们现在进入AudioService的requestAudioFocus继续分析。
慢着,AudioManager中还有一个和requestAudioFocus相关的方法,那就是requestAudioFocusForCall,通过名字可以知道这是跟电话相关的接口,看下源码
public void requestAudioFocusForCall(int streamType, int durationHint) { IAudioService service = getService(); try { service.requestAudioFocus(new AudioAttributes.Builder() .setInternalLegacyStreamType(streamType).build(), durationHint, mICallBack, null, AudioSystem.IN_VOICE_COMM_FOCUS_ID, getContext().getOpPackageName(), AUDIOFOCUS_FLAG_LOCK, null ); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); }}
在代码中全局搜索requestAudioFocusForCall,发现只有CallManager中有调用,而该方法被增加了@hide注解,不过第三方应用可以通过一些特殊方式来调用,这里就不展开讲解了。
留意一下AudioSystem.IN_VOICE_COMM_FOCUS_ID和AUDIOFOCUS_FLAG_LOCK后面会用到。
不知道大家有没有晕呢!我们先来梳理一下吧!以上的代码均是在AudioManager中,其中requestAudioFocus重载了三次,但只有一个是对外开放的。额外看到了一个为电话而生的requestAudioFocusForCall。
AudioService直接上源码
public int requestAudioFocus(AudioAttributes aa, int durationHint, IBinder cb, IAudioFocusDispatcher fd, String clientId, String callingPackageName, int flags, IAudioPolicyCallback pcb) { return mMediaFocusControl.requestAudioFocus(aa, durationHint, cb, fd, clientId, callingPackageName, flags);}
AudioService只是做了中转,并没有做实际的操作,具体实现都是在MediaFocusControl中
下面我们进入MediaFocusControl中
protected int requestAudioFocus(AudioAttributes aa, int focusChangeHint, IBinder cb, IAudioFocusDispatcher fd, String clientId, String callingPackageName, int flags) { synchronized (mAudioFocusLock) { boolean focusGrantDelayed = false; if (!canReassignAudioFocus()) { if ((flags & AudioManager.AUDIOFOCUS_FLAG_DELAY_OK) == 0) { return AudioManager.AUDIOFOCUS_REQUEST_FAILED; } else { focusGrantDelayed = true; } } if (!mFocusStack.empty() && mFocusStack.peek().hasSameClient(clientId)) { final FocusRequester fr = mFocusStack.peek(); if (fr.getGainRequest() == focusChangeHint && fr.getGrantFlags() == flags) { return AudioManager.AUDIOFOCUS_REQUEST_GRANTED; } if (!focusGrantDelayed) { mFocusStack.pop(); fr.release(); } } removeFocusStackEntry(clientId, false , false ); final FocusRequester nfr = new FocusRequester(aa, focusChangeHint, flags, fd, cb, clientId, afdh, callingPackageName, Binder.getCallingUid(), this); if (focusGrantDelayed) { final int requestResult = pushBelowLockedFocusOwners(nfr); return requestResult; } else { if (!mFocusStack.empty()) { propagateFocusLossFromGain_syncAf(focusChangeHint); } mFocusStack.push(nfr); } } return AudioManager.AUDIOFOCUS_REQUEST_GRANTED;}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
注解:
1 这里面出现了mFocusStack变量,栈结构后进先出,用来维护各client的申请和释放,
当满足mFocusStack不为空,并且当前栈顶(peek得到栈顶元素,但是并未出栈)的clientId为AudioSystem.IN_VOICE_COMM_FOCUS_ID,这个熟悉吧!或者fr.isLockedFocusOwner,这个FocusOwner又是什么鬼呢!我们需要进入FocusRequester类中来看,这里的mGrantFlags是在FocusRequester的构造方法中初始化的,其实就是前面传进来的flags,而AUDIOFOCUS_FLAG_LOCK也熟悉吧,没印象的回头看一下requestAudioFocusForCall方法我叫你们留意的两个参数。这段代码的最终含义就是如果正在打电话的过程中,其他应用申请焦点会延迟申请。
private final Stack<FocusRequester> mFocusStack = new Stack<FocusRequester>();private boolean canReassignAudioFocus() { if (!mFocusStack.isEmpty() && isLockedFocusOwner(mFocusStack.peek())) { return false; } return true;}private boolean isLockedFocusOwner(FocusRequester fr) { return (fr.hasSameClient(AudioSystem.IN_VOICE_COMM_FOCUS_ID) || fr.isLockedFocusOwner());}boolean isLockedFocusOwner() { return ((mGrantFlags & AudioManager.AUDIOFOCUS_FLAG_LOCK) != 0);}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
2 该方法中初始化了很多个变量,大概有个印象就好,我们在注解3中会详细讲解其中几个关键变量
FocusRequester(AudioAttributes aa, int focusRequest, int grantFlags, IAudioFocusDispatcher afl, IBinder source, String id, AudioFocusDeathHandler hdlr, String pn, int uid, @NonNull MediaFocusControl ctlr) { mAttributes = aa; mFocusDispatcher = afl; mSourceRef = source; mClientId = id; mDeathHandler = hdlr; mPackageName = pn; mCallingUid = uid; mFocusGainRequest = focusRequest; mGrantFlags = grantFlags; mFocusLossReceived = AudioManager.AUDIOFOCUS_NONE; mFocusController = ctlr;}
3 通知栈中其他元素丢失焦点流程
private void propagateFocusLossFromGain_syncAf(int focusGain) { Iterator<FocusRequester> stackIterator = mFocusStack.iterator(); while (stackIterator.hasNext()) { stackIterator.next().handleExternalFocusGain(focusGain); }}
stackIterator.next()得到的是FocusRequester对象,因此查看FocusRequester中handleExternalFocusGain的源码,这里面假设我们传进来的参数是AudioManager.AUDIOFOCUS_GAIN
void handleExternalFocusGain(int focusGain) { int focusLoss = focusLossForGainRequest(focusGain); handleFocusLoss(focusLoss);}/** * 这个方法比较长,主要关注两个变量gainRequest和mFocusLossReceived * mFocusLossReceived这个值是多少呢!我们发现在注解2中FocusRequester的构造方法中进行的赋值 * mFocusLossReceived = AudioManager.AUDIOFOCUS_NONE; * gainRequest这个是我们传进来的,例如AudioManager.AUDIOFOCUS_GAIN * return AudioManager.AUDIOFOCUS_LOSS * 若我们传进来的参数是AudioManager.AUDIOFOCUS_GAIN_TRANSIENT * 则return AudioManager.AUDIOFOCUS_LOSS_TRANSIENT */private int focusLossForGainRequest(int gainRequest) { switch (gainRequest) { case AudioManager.AUDIOFOCUS_GAIN: switch (mFocusLossReceived) { case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK: case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT: case AudioManager.AUDIOFOCUS_LOSS: case AudioManager.AUDIOFOCUS_NONE: return AudioManager.AUDIOFOCUS_LOSS; } case AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE: case AudioManager.AUDIOFOCUS_GAIN_TRANSIENT: switch (mFocusLossReceived) { case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK: case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT: case AudioManager.AUDIOFOCUS_NONE: return AudioManager.AUDIOFOCUS_LOSS_TRANSIENT; case AudioManager.AUDIOFOCUS_LOSS: return AudioManager.AUDIOFOCUS_LOSS; } case AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK: switch (mFocusLossReceived) { case AudioManager.AUDIOFOCUS_NONE: case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK: return AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK; case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT: return AudioManager.AUDIOFOCUS_LOSS_TRANSIENT; case AudioManager.AUDIOFOCUS_LOSS: return AudioManager.AUDIOFOCUS_LOSS; } default: Log.e(TAG, "focusLossForGainRequest() for invalid focus request " + gainRequest); return AudioManager.AUDIOFOCUS_NONE; }}void handleFocusLoss(int focusLoss) { try { if (focusLoss != mFocusLossReceived) { mFocusLossReceived = focusLoss; final IAudioFocusDispatcher fd = mFocusDispatcher; if (fd != null) { fd.dispatchAudioFocusChange(mFocusLossReceived, mClientId); } } } catch (android.os.RemoteException e) { Log.e(TAG, "Failure to signal loss of audio focus due to:", e); }}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
重点看一下handleFocusLoss方法的
fd.dispatchAudioFocusChange(mFocusLossReceived, mClientId)
通过mFocusDispatcher对象调用了dispatchAudioFocusChange方法,将mFocusLossReceived和mClientId传了进去。现在我们回头一步步看mFocusDispatcher是如何传进来的。
3.1 FocusRequester构造方法的第四个参数IAudioFocusDispatcher afl
3.2 MediaFocusControl的requestAudioFocus方法的第四个参数IAudioFocusDispatcher fd
3.3 AudioService的requestAudioFocus方法的第四个参数IAudioFocusDispatcher fd
最终我们在AudioManager中找到了
private final IAudioFocusDispatcher mAudioFocusDispatcher = new IAudioFocusDispatcher.Stub() { public void dispatchAudioFocusChange(int focusChange, String id) { final Message m = mServiceEventHandlerDelegate.getHandler().obtainMessage( MSSG_FOCUS_CHANGE, focusChange, 0, id); mServiceEventHandlerDelegate.getHandler().sendMessage(m); }};private class ServiceEventHandlerDelegate { private final Handler mHandler; ServiceEventHandlerDelegate(Handler handler) { Looper looper; if (handler == null) { if ((looper = Looper.myLooper()) == null) { looper = Looper.getMainLooper(); } } else { looper = handler.getLooper(); } if (looper != null) { mHandler = new Handler(looper) { @Override public void handleMessage(Message msg) { switch (msg.what) { case MSSG_FOCUS_CHANGE: OnAudioFocusChangeListener listener = null; synchronized (mFocusListenerLock) { listener = findFocusListener((String) msg.obj); } if (listener != null) { Log.d(TAG, "AudioManager dispatching onAudioFocusChange(" + msg.arg1 + ") for " + msg.obj); listener.onAudioFocusChange(msg.arg1); } break; } } }; } else { mHandler = null; } } Handler getHandler() { return mHandler; }}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
在dispatchAudioFocusChange方法中通过mServiceEventHandlerDelegate将事件分发到了另外的线程中,这也是让AudioService从事件分发中抽离出来
OnAudioFocusChangeListener listener = null;synchronized (mFocusListenerLock) { listener = findFocusListener((String) msg.obj);}if (listener != null) { listener.onAudioFocusChange(msg.arg1);}private OnAudioFocusChangeListener findFocusListener(String id) { return mAudioFocusIdListenerMap.get(id);}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
至此我们终于将焦点改变的消息通知到了应用层注册的onAudioFocusChange方法中。
requestAudioFocus方法分析完了,abandonAudioFocus方法完全相同的流程,这里就不做过多介绍了,有兴趣的童鞋可以自己看这源码走一遍流程就可以理解了。
好了,综合前一篇的焦点机制的应用,加上这篇的源码分析,音频焦点也就告一段落了,如果发现有分析错误的地方请及时指出。后续有时间还会再写一些关于音频的文章。