android 音乐焦点,Android音频焦点详解(下)——源码详解

耽搁了几天,最近一直在忙找工作的事情,今天把这篇文章补上。

本文基于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 mAudioFocusIdListenerMap =

new HashMap();

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 mFocusStack = new Stack();

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 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方法完全相同的流程,这里就不做过多介绍了,有兴趣的童鞋可以自己看这源码走一遍流程就可以理解了。

好了,综合前一篇的焦点机制的应用,加上这篇的源码分析,音频焦点也就告一段落了,如果发现有分析错误的地方请及时指出。后续有时间还会再写一些关于音频的文章。

e6583d79e7cdea0f10e10e1ec717e474.png

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值