轉載請註明出處:http://blog.csdn.net/wangjihuanghun/article/details/56069283
耽擱了幾天,最近一直在忙找工作的事情,今天把這篇文章補上。
本文基於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方法完全相同的流程,這裏就不做過多介紹了,有興趣的童鞋可以自己看這源碼走一遍流程就可以理解了。
好了,綜合前一篇的焦點機制的應用,加上這篇的源碼分析,音頻焦點也就告一段落了,如果發現有分析錯誤的地方請及時指出。後續有時間還會再寫一些關於音頻的文章。