Android java層音頻相關的分析與理解(三)調用相關



轉載自:http://blog.csdn.net/u012440406/article/details/51138486

Android中會有多個應用需要支持音頻的播放。當同一時間內有多個應用需要輸出音頻的時候,到底是全部輸出?還是輸出其中一個?假如輸出其中一個,那到底輸出哪個?以什麼標準去界定?爲了處理這些關係。Android在2.3的時候引入了AudioFocus機制並沿用到現在。


1 AudioFocus簡介

AudioFocus是一個沒有優先級概念的搶佔式的機制。一般情況下,最後申請使用AudioFoucs的應用或取得當前的AudioFocus從而將前面申請的應用給停掉。但是,有一種情況是特殊的,就是通話。通話作爲手機的重要功能,從響鈴到通話接受,Telephony都會申請AudioFocus,所有應用都不能從Telephony中獲取音頻焦點,但Telephony可以從任何應用中獲取到AudioFocus。

當前Android6.0下AudioFocus有幾種狀態,分別爲:


從上面AudioFocus的狀態可以看出,其實AudioFocus的狀態是對應的。當後一個應用去GainAudioFocus時,前一個應用就相對應地LossAudioFocus。後一個應用以什麼形式去Gain,前一個應該就會受到相應Loss的通知。但是,當前一個應用收到這個Loss通知時,應用怎麼處理是應用內部的事情。因爲AudioFoucs機制是一個建議性而不是強制性的機制。應用使用這種機制只是使用戶體驗更加好,而不採用這套機制,甚至不遵循這套機制,對應用與系統本身來是,是沒有什麼影響的。

 

再補充說明一下,當應用通過AudioFocus機制去輸出音頻的時候,它可以因爲其他應用通過獲取AudioFocus去暫停輸出。但是,應用也可以不通過AudioFocus去輸出音頻。此時該應用的一切音頻輸出邏輯由本身控制。不受AudioFocus機制影響。


2 AudioFocus的實現


AudioFocus機制其實相當於一個棧。




如上圖所示,AudioFocus機制是以棧的形式實現,位於棧頂的應用能獲取到AudioFocus從而能輸出音頻。非棧頂應用會暫時失去AudioFocus直至位於其上面的全部應用關閉或主動丟失AudioFocus。

AudioFocus的實現是從AudioManager.java開始的。

一般來說,應用內部會通過調用AudioManager的requestAudioFocus()方法去獲取AudioFocus。AudioManager這一次,與以往不同,不只是作爲一個類似於傳遞者的角色直接獲取AudioService的句柄然後將參數傳遞過去,而是先通過一系列的判斷和封裝,通過2次重載,將應用的相關消息保存並調用AudioSercvice中的requestAudioFocus(),進一步處理獲取AudioFocus操作。


所以我們來看一下AudioManager的requestAudioFocus()的主要內容:

[java] view plain copy
  1. <span style="font-size:14px;"><span style="font-size:12px;">public int requestAudioFocus (OnAudioFocusChangeListener l,  
  2.             @NonNull AudioAttributes requestAttributes,  
  3.             int durationHint,  
  4.             int flags,  
  5.             AudioPolicy ap) throws IllegalArgumentException {  
  6.         ……//一系列的參數判斷  
  7.         int status = AUDIOFOCUS_REQUEST_FAILED;  
  8.         //將申請AudioFocus的應用的listener放進mAudioFocusIdListenerMap中  
  9. registerAudioFocusListener(l);  
  10.         IAudioService service = getService();  
  11.         try {  
  12.             //調用AudioService的requestAudioFocus()  
  13.             status = service.requestAudioFocus(requestAttributes, durationHint, mICallBack,  
  14.                     mAudioFocusDispatcher, getIdForAudioFocusListener(l),  
  15.                     getContext().getOpPackageName() /* package name */, flags,  
  16.                     ap != null ? ap.cb() : null);  
  17.         } catch (RemoteException e) {  
  18.             Log.e(TAG, "Can't call requestAudioFocus() on AudioService:", e);  
  19.         }  
  20.         return status;  
  21.     }  
  22. </span></span>  

在requestAudioFocus()中,與以往的稍有不同的是,requestAudioFocus()先一步對傳入的參數進行處理,並將申請應用的listener放進了mAudioFocusIdListenerMap中保存。而傳入的參數中,多了mAudioFocusDispatcher和getIdForAudioFocusListener(l)。getIdForAudioFocusListener(l)很明顯,就是將這個listener的名字傳進去。那mAudioFocusDispatcher到底是幹什麼的呢?

 

接下來,我們先來關注一下mAudioFocusDispatcher:

[java] view plain copy
  1. <span style="font-size:14px;"><span style="font-size:12px;">    private final IAudioFocusDispatcher mAudioFocusDispatcher = new IAudioFocusDispatcher.Stub() {  
  2.   
  3.         public void dispatchAudioFocusChange(int focusChange, String id) {  
  4.             Message m = mAudioFocusEventHandlerDelegate.getHandler().obtainMessage(focusChange, id);  
  5.             mAudioFocusEventHandlerDelegate.getHandler().sendMessage(m);  
  6.         }  
  7.     };  
  8. </span></span>  

從上面我們可以看出,mAudioFocusDispatcher是IaudioFocusDispatcher的一個實現。他裏面有一個方法,負責調用mAudioFocusEventHandlerDelegate去處理消息的內容。那mAudioFocusEventHandlerDelegate又是幹什麼呢?我們繼續往下看。

[java] view plain copy
  1. <span style="font-size:14px;"><span style="font-size:12px;">    private class FocusEventHandlerDelegate {  
  2.         private final Handler mHandler;  
  3.         FocusEventHandlerDelegate() {  
  4.             Looper looper;  
  5.             if ((looper = Looper.myLooper()) == null) {  
  6.                 looper = Looper.getMainLooper();  
  7.             }  
  8.             if (looper != null) {  
  9.                 // implement the event handler delegate to receive audio focus events  
  10.                 mHandler = new Handler(looper) {  
  11.                     @Override  
  12.                     public void handleMessage(Message msg) {  
  13.                         OnAudioFocusChangeListener listener = null;  
  14.                         synchronized(mFocusListenerLock) {  
  15.                             listener = findFocusListener((String)msg.obj);  
  16.                         }  
  17.                         if (listener != null) {  
  18.                             Log.d(TAG, "AudioManager dispatching onAudioFocusChange("  
  19.                                     + msg.what + ") for " + msg.obj);  
  20.                             listener.onAudioFocusChange(msg.what);  
  21.                         }  
  22.                     }  
  23.                 };  
  24.             } else {  
  25.                 mHandler = null;  
  26.             }  
  27.         }  
  28.         Handler getHandler() {  
  29.             return mHandler;  
  30.         }  
  31.     }  
  32. </span></span>  

從上面我們可以看出,調用mAudioFocusEventHandlerDelegate就是爲了回調傳入的listener的onAudioFocusChange方法去處理相關的內容。那明明可以直接通過l.onAudioFocusChange(focusChange)去處理這些信息,爲什麼要多此一舉專門用一個handle去處理了?於是我去查了一下啊,原因如下:因爲這個回調尚在Binder的調用線程中,如果在這裏因爲用戶傳入的listener的代碼有問題而爆出異常或阻塞甚至惡意拖延,則會導致Binder的另一端因異常而崩潰或阻塞。所以到此爲止,AudioService已經盡到了通知的義務,應該通過Handler將後續的操作發往另外一個線程,使AudioService儘可能遠離回調現實的影響。

 

所以,現在我們可以進入AudioService去看一下到底AudioService是怎樣處理的。

AudioService. requestAudioFocus()如下:

[java] view plain copy
  1. <span style="font-size:14px;"><span style="font-size:12px;">public int requestAudioFocus(AudioAttributes aa, int durationHint, IBinder cb,  
  2.             IAudioFocusDispatcher fd, String clientId, String callingPackageName, int flags,  
  3.             IAudioPolicyCallback pcb) {  
  4.         // 檢測權限  
  5.         if ((flags & AudioManager.AUDIOFOCUS_FLAG_LOCK) == AudioManager.AUDIOFOCUS_FLAG_LOCK) {     //奇怪的地方1  
  6.             if (AudioSystem.IN_VOICE_COMM_FOCUS_ID.equals(clientId)) {  
  7.                 if (PackageManager.PERMISSION_GRANTED != mContext.checkCallingOrSelfPermission(  
  8.                             android.Manifest.permission.MODIFY_PHONE_STATE)) {  
  9.                     Log.e(TAG, "Invalid permission to (un)lock audio focus"new Exception());  
  10.                     return AudioManager.AUDIOFOCUS_REQUEST_FAILED;  
  11.                 }  
  12.             } else {  
  13.                 //註冊一個AudioPolicy去獲取AudioFocus  
  14.                 synchronized (mAudioPolicies) {    //奇怪的地方2  
  15.                     if (!mAudioPolicies.containsKey(pcb.asBinder())) {  
  16.                         Log.e(TAG, "Invalid unregistered AudioPolicy to (un)lock audio focus");  
  17.                         return AudioManager.AUDIOFOCUS_REQUEST_FAILED;  
  18.                     }  
  19.                 }  
  20.             }  
  21.         }  
  22.   
  23.         return mMediaFocusControl.requestAudioFocus(aa, durationHint, cb, fd,  
  24.                 clientId, callingPackageName, flags);  
  25.     }  
  26. </span></span>  

在上面假如仔細看的話,有2個地方會覺得很奇怪。第一個,AudioManager.AUDIOFOCUS_FLAG_LOCK ,第二個mAudioPolicies。AudioManager.AUDIOFOCUS_FLAG_LOCK代表着AudioFocus被鎖定的狀態。處於這種狀態下假如應用需要獲取AudioFoucs,首先得去registerAudioPolicy。這就和AudioPolicy相關。

 

在這裏AudioPolicy與AudioPolicyService一點關係的沒有。前者在java層,只是負責提供簡單的音頻路由和音頻焦點的管理;後者處於C++層負責處理音頻相關的路由策略。在這裏,由於我們一般不會用到AudioPoluicy和AudioManager.AUDIOFOCUS_FLAG_LOCK,所以這個會在4.3稍作解析就過了。

 

回到AudioService. requestAudioFocus(),我們其實可以發現,真正處理AudioFocus的,是MediaFocusControl……


所以現在就來看一下MediaFocusControl是怎麼處理的。

[java] view plain copy
  1. <span style="font-size:14px;"><span style="font-size:12px;">   protected int requestAudioFocus(AudioAttributes aa, int focusChangeHint, IBinder cb,  
  2.             IAudioFocusDispatcher fd, String clientId, String callingPackageName, int flags) {  
  3.          ……  
  4.         //跳過各種判斷  
  5.         synchronized(mAudioFocusLock) {  
  6.           ……  
  7.             //通過AudioFocusDeathHandler對申請AudioFocus的應用進行監聽  
  8.             AudioFocusDeathHandler afdh = new AudioFocusDeathHandler(cb);  
  9.             try {  
  10.                 cb.linkToDeath(afdh, 0);  
  11.             } catch (RemoteException e) {  
  12.                 ……  
  13.                 return AudioManager.AUDIOFOCUS_REQUEST_FAILED;  
  14.             }  
  15.               
  16. // mFocusStack就是AudioFocus機制所在的棧  
  17. // 如果申請AudioFocus的應用已經獲取的AudioFocus,不作任何處理  
  18.             if (!mFocusStack.empty() && mFocusStack.peek().hasSameClient(clientId)) {  
  19.                 ……  
  20.             }  
  21.   
  22.             // 如果已經在Stack中已經存在去這個應用的請求,就先remove掉它  
  23.             removeFocusStackEntry(clientId, false /* signal */false /*notifyFocusFollowers*/);  
  24.               
  25.             //新建一個FocusRequester對象  
  26.             final FocusRequester nfr = new FocusRequester(aa, focusChangeHint, flags, fd, cb,  
  27.                     clientId, afdh, callingPackageName, Binder.getCallingUid(), this);  
  28.             if (focusGrantDelayed) {  
  29.                 //處於可延遲獲取AudioFocus狀態(正在通話)下,先把它放進Stack中  
  30.                 final int requestResult = pushBelowLockedFocusOwners(nfr);  
  31.                 if (requestResult != AudioManager.AUDIOFOCUS_REQUEST_FAILED) {  
  32.                     notifyExtPolicyFocusGrant_syncAf(nfr.toAudioFocusInfo(), requestResult);  
  33.                 }  
  34.                 return requestResult;  
  35.             } else {  
  36.                   
  37.                 if (!mFocusStack.empty()) {  
  38.                     //使上一個佔有AudioFocus的應用失去AudioFocus  
  39.                     propagateFocusLossFromGain_syncAf(focusChangeHint);  
  40.                 }  
  41.   
  42.                 //將當前的request放在棧頂  
  43.                 mFocusStack.push(nfr);  
  44.             }  
  45.             //通知系統該應用獲取AudioFocus  
  46.             notifyExtPolicyFocusGrant_syncAf(nfr.toAudioFocusInfo(),  
  47.                     AudioManager.AUDIOFOCUS_REQUEST_GRANTED);  
  48.   
  49.         }//synchronized(mAudioFocusLock)  
  50.   
  51.         return AudioManager.AUDIOFOCUS_REQUEST_GRANTED;  
  52.     }  
  53. </span></span>  

在這裏我們可以看到真正的requestAudioFocus()處理。其實主要的流程就是:

1) 判斷是否需要延遲獲取AudioFocus(通話狀態),判斷應用是否已經擁有AudioFocus等等。

2) 讓上一個佔有AudioFocus的應用失去AudioFocus,並通過一系列的操作回調它的onAudioFocusChange方法。(流程較簡單,不詳細敘述了)

3) 將當前應用FocusRequester放到棧頂,並通知系統當前應用獲取AudioFocus


失去AudioFocus的操作(abandonAudioFocus)和requestAudioFocus是大同小異的。所以在這裏也不詳細說明了。


3 AudioPolicy的使用

最後在這裏簡單說一下java層的AudioPolicy的理解。AudioService會有一個HashMap的對象mAudioPolicies,裏面保存着所有registerAudioPolicy()過的應用的信息。通過registerAudioPolicy()我們可以對AudioFocus進行鎖定並不允許不註冊AudioPolicy的應用修改。但是這個功能其實我們大部分時候都不會用到。

音頻調用相關就簡單分析到這。再重申一下,AudioFocus是一個建議非強制機制,我們不適用AudioFocus都不會影響應用的音頻輸出與輸入。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章