1.调用示例
packages/apps/Car/LocalMediaPlayer/src/com/android/car/media/localmediaplayer/Player.java
private boolean requestAudioFocus(Runnable onSuccess) { int result = mAudioManager.requestAudioFocus(mAudioFocusListener, AudioManager.STREAM_MUSIC,AudioManager.AUDIOFOCUS_GAIN); if (result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) { onSuccess.run(); return true; } return false; }
2.根据调用参数,找到对应的实现
media/java/android/media/AudioManager.java
public int requestAudioFocus(OnAudioFocusChangeListener l, int streamType, int durationHint) { ... status = requestAudioFocus(l, new AudioAttributes.Builder() .setInternalLegacyStreamType(streamType).build(), durationHint, 0 /* flags, legacy behavior */); ... return status; }
接口前面的英文注释没贴上来,挑选重要的
参数:durationHint
AUDIOFOCUS_GAIN_TRANSIENT
表明请求是短暂的,临时的,焦点很快会被放弃。比如播放驾驶转向,或者notifications sounds.
AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK
表明之前的焦点owner可以继续播放,但是需要ducks自己的output,压低自己的声音,供申请者做一个短暂的播放。(想象电台主播那种效果)
AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE
对于从系统中获益的临时请求,不会播放诸如通知之类的破坏性声音,用于诸如语音备忘录录音或语音识别之类的场景)。EXCLUSIVE是独家的意思,TRANSIENT是短暂的意思
简单来理解就是
我的请求是短暂的,但是我不希望这时候有嘟嘟之类的notifications声音打扰,我要独占
AUDIOFOCUS_GAIN
for a focus request of unknown duration such as the playback of a song or a video.
中间经过各种弯弯绕绕,如果按照最普通的调用方式,都可以不用关注了。
直接可以去看实现了:
services/core/java/com/android/server/audio/AudioService.java
public int requestAudioFocus(AudioAttributes aa, int durationHint, IBinder cb, IAudioFocusDispatcher fd, String clientId, String callingPackageName, int flags,IAudioPolicyCallback pcb, int sdk) { // permission checks if ((flags & AudioManager.AUDIOFOCUS_FLAG_LOCK) == AudioManager.AUDIOFOCUS_FLAG_LOCK) { if (AudioSystem.IN_VOICE_COMM_FOCUS_ID.equals(clientId)) { if (PackageManager.PERMISSION_GRANTED != mContext.checkCallingOrSelfPermission( android.Manifest.permission.MODIFY_PHONE_STATE)) { Log.e(TAG, "Invalid permission to (un)lock audio focus", new Exception()); return AudioManager.AUDIOFOCUS_REQUEST_FAILED; } } else { // only a registered audio policy can be used to lock focus synchronized (mAudioPolicies) { if (!mAudioPolicies.containsKey(pcb.asBinder())) { Log.e(TAG, "Invalid unregistered AudioPolicy to (un)lock audio focus"); return AudioManager.AUDIOFOCUS_REQUEST_FAILED; } } } } return mMediaFocusControl.requestAudioFocus(aa, durationHint, cb, fd, clientId, callingPackageName, flags, sdk); }
暂时没搞懂这个AUDIOFOCUS_FLAG_LOCK意味着什么。所以,继续走
services/core/java/com/android/server/audio/MediaFocusControl.java
//requestAudioFocus@MediaFocusControl protected int requestAudioFocus(AudioAttributes aa, int focusChangeHint, IBinder cb, IAudioFocusDispatcher fd, String clientId, String callingPackageName, int flags,int sdk) { ... if (!mFocusStack.empty() && mFocusStack.peek().hasSameClient(clientId)) { //取出栈顶 final FocusRequester fr = mFocusStack.peek(); if (fr.getGainRequest() == focusChangeHint && fr.getGrantFlags() == flags) { //栈顶的GainRequest和GrantFlags和完全和新来的请求一致 cb.unlinkToDeath(afdh, 0); notifyExtPolicyFocusGrant_syncAf(fr.toAudioFocusInfo(), AudioManager.AUDIOFOCUS_REQUEST_GRANTED); return AudioManager.AUDIOFOCUS_REQUEST_GRANTED; } //前面的if没有return //说明请求原因不一致,把栈顶拿掉 if (!focusGrantDelayed) { mFocusStack.pop(); // the entry that was "popped" is the same that was "peeked" above fr.release(); } } //到这,栈顶之前的焦点owner已经不存在了 ******************************* // 底下这个函数干的事情主要是这样 // 栈顶的ClientID同新请求一致(reason不一致),pop it, //然后,(注意这个)发出失去焦点的通知... //else 遍历整个stack,有一致的ClientId(reason不一致),pop it, //因为不在栈顶,所以不持有焦点,所以不用通知! removeFocusStackEntry(clientId, false /* signal */, false /*notifyFocusFollowers*/); final FocusRequester nfr = new FocusRequester(aa, focusChangeHint, flags, fd, cb, clientId, afdh, callingPackageName, Binder.getCallingUid(), this, sdk); if (focusGrantDelayed) { ... } else { if (!mFocusStack.empty()) { //翻译下函数名字:广播_焦点失去_从获得 propagateFocusLossFromGain_syncAf(focusChangeHint, nfr); } // 将焦点请求nrf推到mFocusStack栈顶 mFocusStack.push(nfr); //处理_焦点获得_从请求 //焦点的状态切换 //请求-获得-失去 nfr.handleFocusGainFromRequest(AudioManager.AUDIOFOCUS_REQUEST_GRANTED); } ... }
我们来研究下这个mFocusStack
private final Stack<