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

来源:互联网 发布:手机一直恢复系统数据 编辑:程序博客网 时间:2024/06/05 11:32

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


本文基于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);
  • 1

具体请参考我的上篇博客点击这里


从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;}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

根据传进来的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 /* no AudioPolicy*/);}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

这里面对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);    //获取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;}
  • 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 /* policy token */);    } catch (RemoteException e) {        throw e.rethrowFromSystemServer();    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

在代码中全局搜索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);}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

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
  • 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方法我叫你们留意的两个参数。这段代码的最终含义就是如果正在打电话的过程中,其他应用申请焦点会延迟申请。

//MediaFocusControl.javaprivate 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.javaboolean 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;}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

3 通知栈中其他元素丢失焦点流程

//遍历mFocusStack,调用FocusRequester对象的handleExternalFocusGain方法private void propagateFocusLossFromGain_syncAf(int focusGain) {    Iterator<FocusRequester> stackIterator = mFocusStack.iterator();    while (stackIterator.hasNext()) {        stackIterator.next().handleExternalFocusGain(focusGain);    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

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);    }}
  • 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);
  • 1

通过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;    }}
  • 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) {    //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);}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

至此我们终于将焦点改变的消息通知到了应用层注册的onAudioFocusChange方法中。 
requestAudioFocus方法分析完了,abandonAudioFocus方法完全相同的流程,这里就不做过多介绍了,有兴趣的童鞋可以自己看这源码走一遍流程就可以理解了。


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

前两天拍的

版权声明:本文为博主原创文章,未经博主允许不得转载。
原创粉丝点击