歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux編程 >> Linux編程 >> Android中的Audio播放:競爭Audio之Audio Focus的應用

Android中的Audio播放:競爭Audio之Audio Focus的應用

日期:2017/3/1 10:25:27   编辑:Linux編程

Android是多任務系統,Audio系統是競爭資源。Android2.2之前,沒有內建的機制來解決多個程序競爭Audio的問題,2.2引入了稱作AudioFocus的機制來管理對Audio資源的競爭的管理與協調。本文主要講解AudioFocus的使用。

按照AudioFocus的機制,在使用AudioStream之前,需要申請AudioFocus,在獲得AudioFocus之後才可以使用相應的AudioStream;如果有別的程序競爭你正在使用的AudioStream,你的程序需要在收到通知之後做停止播放或者降低聲音的處理。值得指出的是,這種機制是需要合作完成的,需要所有使用Audio資源的程序都按照這種機制來做,而如果有程序在它失去AudioFocus的時候仍然在使用Audio,AudioFocus拿它也沒辦法。而這一點對於開放系統的Android來說很致命的:用戶可能安裝沒遵守這種機制的程序,或者版本太老還沒引入這種機制的程序,這最終會導致很差的用戶體驗。

對於手機方案公司來說,要做的能做的事情就是教育和培訓團隊成員以保證自己內建的程序遵守機制沒問題,這包括了Android原生的程序、自己開發的程序,以及適配第三方的程序。

一、AudioFocus的申請與釋放

下面看與AudioFocus的相關的類:


獲取/放棄AudioFocus的方法都在android.media.AudioManager中,獲取AudioFocus用requestAudioFocus();用完之後,放棄AudioFocus,用abandonAudioFocus()

其中,參數

  • streamType《Android中的Audio播放:音量和遠程播放控制》中說明的AudioStream,其值取決於AudioManager中的STREAM_xxx;
  • durationHint是持續性的指示:
    • AUDIOFOCUS_GAIN指示申請得到的Audio Focus不知道會持續多久,一般是長期占有;
    • AUDIOFOCUS_GAIN_TRANSIENT指示要申請的AudioFocus是暫時性的,會很快用完釋放的;
    • AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK不但說要申請的AudioFocus是暫時性的,還指示當前正在使用AudioFocus的可以繼續播放,只是要“duck”一下(降低音量)。
  • AudioManager.OnAudioFocusChangeListener是申請成功之後監聽AudioFocus使用情況的Listener,後續如果有別的程序要競爭AudioFocus,都是通過這個Listener的onAudioFocusChange()方法來通知這個Audio Focus的使用者的。

返回值,可能是:

  • AUDIOFOCUS_REQUEST_GRANTED:申請成功;
  • AUDIOFOCUS_REQUEST_FAILED:申請失敗。

二、AudioFocus被搶占與重新獲得

由上節中知道,申請/釋放AudioFocus時傳入了AudioManager.OnAudioFocusChangeListener這個參數,其onAudioFocusChange()方法是Audio Focus被搶占與再次獲得通知的地方。所以,每個要使用AudioFocus的程序都要小心實現這個函數,保證AudioFocus實現的一致性。

onAudioFocusChange()方法的focusChange參數指示了該AudioFocus的競爭者對AudioFocus的擁有情況,取值如下:

  • AUDIOFOCUS_GAIN:獲得了Audio Focus;
  • AUDIOFOCUS_LOSS:失去了Audio Focus,並將會持續很長的時間。這裡因為可能會停掉很長時間,所以不僅僅要停止Audio的播放,最好直接釋放掉Media資源。而因為停止播放Audio的時間會很長,如果程序因為這個原因而失去AudioFocus,最好不要讓它再次自動獲得AudioFocus而繼續播放,不然突然冒出來的聲音會讓用戶感覺莫名其妙,感受很不好。這裡直接放棄AudioFocus,當然也不用再偵聽遠程播放控制【如下面代碼的處理】。要再次播放,除非用戶再在界面上點擊開始播放,才重新初始化Media,進行播放。
  • AUDIOFOCUS_LOSS_TRANSIENT:暫時失去Audio Focus,並會很快再次獲得。必須停止Audio的播放,但是因為可能會很快再次獲得AudioFocus,這裡可以不釋放Media資源;
  • AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:暫時失去AudioFocus,但是可以繼續播放,不過要在降低音量。

下面是onAudioFocusChange()方法處理的代碼片段:

  1. OnAudioFocusChangeListener afChangeListener = new OnAudioFocusChangeListener() {
  2. public void onAudioFocusChange(int focusChange) {
  3. if (focusChange == AudioManager.AUDIOFOCUS_LOSS_TRANSIENT
  4. // Pause playback
  5. } else if (focusChange == AudioManager.AUDIOFOCUS_LOSS) {
  6. am.unregisterMediaButtonEventReceiver(RemoteControlReceiver);
  7. am.abandonAudioFocus(afChangeListener);
  8. // Stop playback
  9. } else if (focusChange == AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK) {
  10. // Lower the volume
  11. } else if (focusChange == AudioManager.AUDIOFOCUS_GAIN) {
  12. // Resume playback or Raise it back to normal
  13. }
  14. }
  15. };

三、典型的應用AudioFocus的場景

下面的時序圖描述了AudioFocus被搶占與再次獲取的典型場景:


Audio Focus被搶占與再次獲取的時序圖

注意為了描述簡單,此圖中除了兩個競爭Audio Focus的App之外,只用AudioManager表征了Android的AudioFocus機制中內部參與的對象,實際AudioManager只是外部的表象,內部參與的對象很多,回調函數也並非簡單的直接由AudioManager調用,其中還包含了復雜的IPC機制。

圖中:

  • AudioFocus Client通過requestAudioFocus()獲取AudioFocus,在獲得AudioFocus之後,開始播放Audio[Step#1 ~ #2];
  • 其它程序(Other App)也通過requestAudioFocus()獲取同類AudioStream的AudioFocus [Step#3]
  • AudioFocus Client失去了Audio Focus,在onAudioFocusChanged()中,根據focusChange的值,做第二節中的處理[Step#4];
  • 其它程序(Other App)獲取Audio Focus之後,開始播放Audio[Step#5];
  • 其它程序(Other App)使用Audio之後,通過abandonAudioFocus()歸還AudioFocus [Step#6];
  • AudioFocus Client重新獲得了Audio Focus,可做進一步的處理 [Step#7]

小結

Audio Focus機制要參與各方充分理解並統一遵照施行,有沒有遵照者或者實現有誤的程序存在就可能打破這一機制,帶來糟糕的用戶體驗。在保證Built-in程序沒問題的前提下,如果進入AndroidMarket之前的程序都嚴格執行了AudioFocus相關的測試,應該也沒問題。

相關閱讀:Android中的Audio播放:控制Audio輸出通道切換 http://www.linuxidc.com/Linux/2012-04/57901.htm

問題點以及進一步的探討

  • 內部裁決機制怎樣的?
  • 申請的不同Audio Stream之間是不存在競爭的嗎?

更多Android相關信息見Android 專題頁面 http://www.linuxidc.com/topicnews.aspx?tid=11

Copyright © Linux教程網 All Rights Reserved