舊焦點處理
示例調用鏈:
- requestAudioFocus() → propagateFocusLossFromGain_syncAf() → handleFocusLossFromGain()。
- 系統事件(如來電)→ 強制焦點變化 → handleFocusLossFromGain()。
函數
propagateFocusLossFromGain_syncAf 焦點持有者發生的焦點丟失通知
主要功能:
? 當新的音頻焦點請求到來時,通知當前所有焦點持有者可能發生的焦點丟失? 根據新的焦點增益類型決定哪些現有焦點持有者需要永久放棄焦點
? 清理那些需要永久放棄焦點的持有者
關鍵處理邏輯:
? 遍歷當前焦點棧中的所有持有者,通知它們新的焦點變化? 對于多音頻焦點模式,同樣處理額外的焦點持有者列表
? 收集所有需要永久放棄焦點的持有者并移除它們
參數作用:
? focusGain: 新請求的焦點增益類型(如AUDIOFOCUS_GAIN等)? fr: 新的焦點請求者對象
? forceDuck: 是否強制應用ducking效果(降低音量而非完全停止)
多音頻焦點支持:
? 當啟用多音頻焦點(mMultiAudioFocusEnabled)時,會額外處理mMultiAudioFocusList中的焦點持有者
/*** 當新的焦點請求到來時,傳播相關的焦點丟失通知到當前焦點棧中的各個持有者。* 同時會移除那些收到永久性焦點丟失的棧條目。* * @param focusGain 新的焦點增益類型,將被添加到棧頂* @param fr 新的焦點請求者* @param forceDuck 是否強制應用 ducking 效果*/
@GuardedBy("mAudioFocusLock")
private void propagateFocusLossFromGain_syncAf(int focusGain, final FocusRequester fr,boolean forceDuck) {if (DEBUG) {Log.i(TAG, "propagateFocusLossFromGain_syncAf gain:" + focusGain);}// 創建一個列表來存儲需要移除的客戶端IDfinal List<String> clientsToRemove = new LinkedList<String>();// 遍歷音頻焦點棧,通知所有條目關于新的外部焦點增益if (!mFocusStack.empty()) {for (FocusRequester focusLoser : mFocusStack) {if (DEBUG) {Log.i(TAG, "propagateFocusLossFromGain_syncAf checking client:"+ focusLoser.getClientId());}// 處理焦點丟失,并判斷是否是永久性丟失final boolean isDefinitiveLoss =focusLoser.handleFocusLossFromGain(focusGain, fr, forceDuck);if (isDefinitiveLoss) {// 如果是永久性丟失,則添加到移除列表clientsToRemove.add(focusLoser.getClientId());}}} else if (DEBUG) {Log.i(TAG, "propagateFocusLossFromGain_syncAf empty stack");}// 如果啟用了多音頻焦點且列表不為空,同樣處理這些焦點持有者if (mMultiAudioFocusEnabled && !mMultiAudioFocusList.isEmpty()) {for (FocusRequester multifocusLoser : mMultiAudioFocusList) {final boolean isDefinitiveLoss =multifocusLoser.handleFocusLossFromGain(focusGain, fr, forceDuck);if (isDefinitiveLoss) {clientsToRemove.add(multifocusLoser.getClientId());}}}// 移除所有需要移除的焦點持有者for (String clientToRemove : clientsToRemove) {removeFocusStackEntry(clientToRemove, false /*signal*/,true /*notifyFocusFollowers*/);}
}
handleFocusLossFromGain
主要執行場景:
-
當有新的音頻焦點請求時
? 當一個新的應用請求音頻焦點(如通過 requestAudioFocus())時,系統需要通知當前持有焦點的應用可能會失去焦點。? 該方法會被調用來檢查當前焦點持有者是否需要因為新的焦點請求而失去焦點(或調整行為,如 ducking)。
-
焦點棧變化時
? 在 propagateFocusLossFromGain_syncAf() 方法中,會遍歷當前的焦點棧,對每個焦點持有者調用 handleFocusLossFromGain,以決定它們是否需要失去焦點或調整行為。 -
強制焦點變化時
? 在某些系統事件(如來電、通知、系統強制打斷)時,會觸發焦點重新分配,該方法會被調用來處理當前焦點持有者的狀態。
方法的作用:
? 該方法會根據新的焦點請求類型(如 AUDIOFOCUS_GAIN、AUDIOFOCUS_GAIN_TRANSIENT 等)和當前焦點持有者的狀態,決定是否需要:
? 完全失去焦點(AUDIOFOCUS_LOSS)。
? 臨時失去焦點(如 AUDIOFOCUS_LOSS_TRANSIENT)。
? 降低音量(ducking,AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK)。
? 返回 true 表示焦點持有者需要被移除出焦點棧(永久失去焦點),false 表示可以保留在棧中(如臨時失去焦點后可能恢復)。
/*** 處理由于給定焦點增益導致的焦點丟失。* @param focusGain 導致焦點丟失的焦點增益類型* @param frWinner 新的焦點所有者* @param forceDuck 是否強制閃避* @return 如果焦點丟失是永久性的則返回true,否則返回false*/
@GuardedBy