转载请注明出处:http://www.jianshu.com/p/e5785dcba952
本文基于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 /* flags, legacy behavior */);
} catch (IllegalArgumentException e) {
Log.e(TAG, "Audio focus request denied due to ", e);
}
return status;
}
根据传进来的streamType,构造了一个AudioAttributes对象向下传递,这个AudioAttributes主要是存储了一些音频流信息的属性,后面会用到。接着看
@SystemApi
public int requestAudioFocus(OnAudioFocusChangeListener l,
@NonNull AudioAttributes requestAttributes,
int durationHint,
int flags) throws IllegalArgumentException {
return requestAudioFocus(l, requestAttributes, durationHint,
flags & AUDIOFOCUS_FLAGS_APPS,
null /* no AudioPolicy*/);
}
这里面对falgs进行了与操作,由于之前传进来的是0,所以转换后的结果还是0。接着看
@SystemApi
public int requestAudioFocus(OnAudioFocusChangeListener l,
@NonNull AudioAttributes requestAttributes,
int durationHint,
int flags,
AudioPolicy ap) throws IllegalArgumentException {
// 参数检查
//...
int status = AUDIOFOCUS_REQUEST_FAILED;
registerAudioFocusListener(l);
//获取AudioService实例,这里采用了binder通信,我们只需要知道从此处开始将会进入AudioServie的requestAudioFocus方法
IAudioService service = getService();
try {
status = service.requestAudioFocus(requestAttributes, durationHint, mICallBack,
mAudioFocusDispatcher, getIdForAudioFocusListener(l),
getContext().getOpPackageName() /* package name */, flags,
ap != null ? ap.cb() : null);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
return status;
}
这里面重点看一下 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());
}
}
这段代码比较好理解了,我们根据“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 /* policy token */);
} 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) {
//权限检查,这里面就用到了AudioSystem.IN_VOICE_COMM_FOCUS_ID
//也就是说如果我们的clientId等于AudioSystem.IN_VOICE_COMM_FOCUS_ID
//要申请MODIFY_PHONE_STATE的权限,否则会申请焦点失败。
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) {
//...基础检查
//这一块定义了局部变量focusGrantDelayed,从名字上可以知道延迟申请焦点的意思
//而且只有当canReassignAudioFocus()返回true的时候,focusGrantDelayed才为true,也就是需要延迟申请,详见下方注解1.
synchronized (mAudioFocusLock) {
boolean focusGrantDelayed = false;
if (!canReassignAudioFocus()) {
if ((flags & AudioManager.AUDIOFOCUS_FLAG_DELAY_OK) == 0) {
return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
} else {
focusGrantDelayed = true;
}
}
//如果mFocusStack不为空,并且栈顶的clientId与要申请焦点的clientId相同
if (!mFocusStack.empty() && mFocusStack.peek().hasSameClient(clientId)) {
//得到栈顶元素的FocusRequester对象
final FocusRequester fr = mFocusStack.peek();
if (fr.getGainRequest() == focusChangeHint && fr.getGrantFlags() == flags) {
//如果申请的时长和flags都相同,则表示重复申请,直接返回成功
return AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
}
//非延迟申请
if (!focusGrantDelayed) {
//如果如果申请的时长和flags有一个不相同,则认为需要重新申请,此时需要将栈顶的元素出栈
mFocusStack.pop();
fr.release();
}
}
/*说了这么多可能不太好理解,这里举个🌰
* 先调用mAudioManager.requestAudioFocus(mAudioFocusChange, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN);
* 再调用mAudioManager.requestAudioFocus(mAudioFocusChange, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN_TRANSIENT);
* 两次的申请时长不同,因此会将前一次的申请出栈,然后再处理新的申请
**/
//移除可能在栈中其他位置存在着相同clientId的元素
removeFocusStackEntry(clientId, false /* signal */, false /*notifyFocusFollowers*/);
//构造FocusRequester对象,详见下方注解2.
final FocusRequester nfr = new FocusRequester(aa, focusChangeHint, flags, fd, cb,
clientId, afdh, callingPackageName, Binder.getCallingUid(), this);
//我们前面分析了什么情况下focusGrantDelayed为true,这里重复一遍,也就是我们在打电话的过程中,音乐去申请焦点。
if (focusGrantDelayed) {
//将其插入栈中,什么位置呢?遍历mFocusStack,从栈顶开始isLockedFocusOwner(前面介绍过该放法)为true的元素的下方。
final int requestResult = pushBelowLockedFocusOwners(nfr);
return requestResult;
} else {
if (!mFocusStack.empty()) {
//该方法很重要,通知栈中其他元素丢失焦点,详见下方注解3.
propagateFocusLossFromGain_syncAf(focusChangeHint);
}
//将FocusRequester对象压入栈中
mFocusStack.push(nfr);
}
}
return AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
}
注解:
1 这里面出现了mFocusStack变量,栈结构后进先出,用来维护各client的申请和释放,
当满足mFocusStack不为空,并且当前栈顶(peek得到栈顶元素,但是并未出栈)的clientId为AudioSystem.IN_VOICE_COMM_FOCUS_ID,这个熟悉吧!或者fr.isLockedFocusOwner,这个FocusOwner又是什么鬼呢!我们需要进入FocusRequester类中来看,这里的mGrantFlags是在FocusRequester的构造方法中初始化的,其实就是前面传进来的flags,而AUDIOFOCUS_FLAG_LOCK也熟悉吧,没印象的回头看一下requestAudioFocusForCall方法我叫你们留意的两个参数。这段代码的最终含义就是如果正在打电话的过程中,其他应用申请焦点会延迟申请。
//MediaFocusControl.java
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());
}
//FocusRequester.java
boolean isLockedFocusOwner() {
return ((mGrantFlags & AudioManager.AUDIOFOCUS_FLAG_LOCK) != 0);
}
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 通知栈中其他元素丢失焦点流程
//遍历mFocusStack,调用FocusRequester对象的handleExternalFocusGain方法
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) {
//下面分别看一下focusLossForGainRequest和handleFocusLoss
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);
}
}
重点看一下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/*what*/, focusChange/*arg1*/, 0/*arg2 ignored*/, id/*obj*/);
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) {
// implement the event handler delegate to receive events from audio service
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;
}
}
在dispatchAudioFocusChange方法中通过mServiceEventHandlerDelegate将事件分发到了另外的线程中,这也是让AudioService从事件分发中抽离出来
OnAudioFocusChangeListener listener = null;
synchronized (mFocusListenerLock) {
//msg.obj就是clientId
listener = findFocusListener((String) msg.obj);
}
if (listener != null) {
//我们得到了listener之后回调onAudioFocusChange
//如果当前申请的焦点时长为AudioManager.AUDIOFOCUS_GAIN,则msg.arg1=AudioManager.AUDIOFOCUS_LOSS
//如果当前申请的焦点时长为AudioManager.AUDIOFOCUS_GAIN_TRANSIENT,则msg.arg1=AudioManager.AUDIOFOCUS_LOSS_TRANSIENT
//这个转换是在FocusRequester的focusLossForGainRequest方法中进行的
listener.onAudioFocusChange(msg.arg1);
}
//根据clientId在mAudioFocusIdListenerMap中返回对应的OnAudioFocusChangeListener
//mAudioFocusIdListenerMap是在我们最初调用requestAudioFocus时存储的,不记得的童鞋可以回头看一下
private OnAudioFocusChangeListener findFocusListener(String id) {
return mAudioFocusIdListenerMap.get(id);
}
至此我们终于将焦点改变的消息通知到了应用层注册的onAudioFocusChange方法中。
requestAudioFocus方法分析完了,abandonAudioFocus方法完全相同的流程,这里就不做过多介绍了,有兴趣的童鞋可以自己看这源码走一遍流程就可以理解了。
好了,综合前一篇的焦点机制的应用,加上这篇的源码分析,音频焦点也就告一段落了,如果发现有分析错误的地方请及时指出。后续有时间还会再写一些关于音频的文章。
前两天拍的
网友评论