【轉】解決在Android裝置播放音訊與其他應用重音的問題,並監聽耳機的控制按鈕
概述
在安卓開發中免不了需要播放一點音樂了,音訊了。但是這時候有別的應用正在播放,這時候就會出現重音的現象,完全影響使用者體驗,我們的專案就遇上了這樣的尷尬,然後查找了一些文件,記錄一下;
管理音訊焦點
從谷歌開發瞭解到,這跟音訊的焦點又關係,我們可以獲取當前音訊的焦點來解決問題;
有多個應用程式可能播放音訊,重要的是要考慮他們應該如何互動。為了避免每個音樂應用程式同時播放,Android使用音訊焦點來控制音訊播放 - 只有擁有音訊焦點的應用程式才能播放音訊。
在您的應用程式開始播放音訊之前,應該請求並接收音訊焦點。同樣,它應該知道如何監聽音訊焦點的丟失,並在發生這種情況時適當地做出反應。
請求音訊焦點
在應用開始播放任何音訊之前,自己的應用應該保留將使用的流的音訊焦點,簡單說就是我要現在的焦點,其他人靠邊,但是別人同樣也可能有這樣的操作,我們也需要監聽自己播放的焦點的變化,增加自己程式的相容性。這是通過呼叫requestAudioFocus()完成的,如果您的請求成功,它返回AUDIOFOCUS_REQUEST_GRANTED。
您必須指定您使用的流,以及是否需要暫時或永久的音訊焦點。當您希望只在短時間內播放音訊時請求暫時聚焦(例如在播放導航指示時)。當您計劃在可預見的未來播放音訊時請求永久音訊聚焦(例如,播放音樂時)
以下程式碼片段請求音樂音訊流的永久音訊焦點。您應該在開始播放之前立即請求音訊焦點,例如當用戶按下播放或下一個遊戲關卡的背景音樂開始時。
AudioManager am = mContext.getSystemService(Context.AUDIO_SERVICE); ... // Request audio focus for playback int result = am.requestAudioFocus(afChangeListener, // Use the music stream. AudioManager.STREAM_MUSIC, // Request permanent focus. AudioManager.AUDIOFOCUS_GAIN); if (result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) { am.registerMediaButtonEventReceiver(RemoteControlReceiver); // Start playback. }
上述程式碼你就可以得到焦點播放你的音訊,但是一旦你完成播放,一定要呼叫abandonAudioFocus()。這會通知系統您不再需要焦點和登出相關聯的AudioManager.OnAudioFocusChangeListener。在放棄瞬態焦點的情況下,這允許任何中斷的應用程式繼續播放。
// Abandon audio focus when playback complete am.abandonAudioFocus(afChangeListener);
但是有些時候我們並不想完全停掉另一個程式的聲音,比如這時候正在進行高德導航,或者微信語音,希望另一個程式的聲音小點,不要蓋過自己即可,開車導航聽音樂的時候,我們應該遇到過;
當請求瞬態音訊焦點時,您有一個附加選項:是否要啟用“低音”。通常,當良好的音訊應用程式失去音訊焦點時,它立即使其播放靜音。通過請求一個允許迴避的暫時音訊焦點,你告訴其他音訊應用程式它們可以接受他們繼續播放,只要他們降低音量,直到焦點回到他們。
// Request audio focus for playback int result = am.requestAudioFocus(afChangeListener, // Use the music stream. AudioManager.STREAM_MUSIC, // Request permanent focus. AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK); if (result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) { // Start playback. }
每當另一個應用程式請求音訊焦點如上所述,其永久和瞬態(有或沒有支援低音)之間的選擇音訊焦點由您在請求焦點時註冊的偵聽器接收。
處理音訊焦點的丟失
如果您的應用程式可以請求音訊焦點,那麼當其他應用程式請求焦點時,它會反過來失去焦點。您的應用程式如何響應音訊焦點的丟失取決於丟失的方式。
您請求音訊焦點時註冊的音訊焦點更改偵聽器的onAudioFocusChange()回撥方法接收到描述焦點更改事件的引數。具體來說,可能的聚焦丟失事件映象來自前一部分的聚焦請求型別 - 永久丟失,瞬時丟失和允許具有迴避的瞬態。
一般來說,暫時的(臨時的)音訊焦點的丟失應導致您的應用程式使它的音訊流靜音,否則保持相同的狀態。您應該繼續監視音訊焦點的變化,並準備在恢復焦點後暫停播放。
如果音訊焦點丟失是永久性的,假設另一個應用程式現在正用於聽音訊,您的應用程式應該有效地結束自己。實際上,這意味著停止播放,刪除媒體按鈕偵聽器 - 允許新的音訊播放器專門處理這些事件 - 並放棄您的音訊焦點。此時,您需要在恢復播放音訊之前需要執行使用者操作(在應用程式中按下播放)。
在下面的程式碼片段中,如果音訊丟失是暫時的,我們暫停播放我們的媒體播放器物件,並在我們恢復焦點後重新開始播放。如果丟失是永久性的,它將取消註冊我們的媒體按鈕事件接收器,並停止監視音訊焦點更改。
AudioManager.OnAudioFocusChangeListener afChangeListener = new AudioManager.OnAudioFocusChangeListener() { public void onAudioFocusChange(int focusChange) { if (focusChange == AUDIOFOCUS_LOSS_TRANSIENT) { // Pause playback } else if (focusChange == AudioManager.AUDIOFOCUS_GAIN) { // Resume playback } else if (focusChange == AudioManager.AUDIOFOCUS_LOSS) { am.unregisterMediaButtonEventReceiver(RemoteControlReceiver); am.abandonAudioFocus(afChangeListener); // Stop playback } } };
處理音訊焦點瞬間丟失
在瞬時丟失音訊焦點的情況下,允許duck(迴避),而不是暫停回放。
OnAudioFocusChangeListener afChangeListener = new OnAudioFocusChangeListener() { public void onAudioFocusChange(int focusChange) { if (focusChange == AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK) { // Lower the volume } else if (focusChange == AudioManager.AUDIOFOCUS_GAIN) { // Raise it back to normal } } };
至此,我們就可以隨心所欲的處理我們音訊播放的焦點問題了
處理音訊輸出硬體
當用戶從他們的Android裝置享受音訊時,使用者有很多選擇。大多數裝置都有內建揚聲器,有線耳機的耳機插孔,許多裝置還具有藍芽連線和支援A2DP音訊。
您的應用程式的行為可能會受到輸出路由到的硬體的影響。
您可以查詢AudioManager以確定音訊當前是否路由到裝置揚聲器,有線耳機或連線的藍芽裝置,如以下程式碼段所示:
if (isBluetoothA2dpOn()) { // Adjust output for Bluetooth. } else if (isSpeakerphoneOn()) { // Adjust output for Speakerphone. } else if (isWiredHeadsetOn()) { // Adjust output for headsets } else { // If audio plays and noone can hear it, is it still playing? }
處理音訊輸出硬體的更改
現在各種的音樂播放器都支援在拔掉耳機或者藍芽的情況正在播放的音視訊暫停,這是個很實用的功能,當然你也有必要支援一下;
當耳機拔掉或藍芽裝置斷開連線時,音訊流自動重新路由到內建揚聲器。如果你聽到你的音樂高達我的音量,這可能是一個嘈雜的驚喜。
幸運的是,當發生這種情況時,系統廣播ACTION_AUDIO_BECOMING_NOISY Intent。這是一個好的習慣,註冊一個BroadcastReceiver監聽這個Intent,無論你在播放音訊。在音樂播放器的情況下,使用者通常期望暫停播放,而對於遊戲,可以選擇顯著降低音量。
private class NoisyAudioStreamReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { if (AudioManager.ACTION_AUDIO_BECOMING_NOISY.equals(intent.getAction())) { // Pause the playback } } } private IntentFilter intentFilter = new IntentFilter(AudioManager.ACTION_AUDIO_BECOMING_NOISY); private void startPlayback() { registerReceiver(myNoisyAudioStreamReceiver(), intentFilter); } private void stopPlayback() { unregisterReceiver(myNoisyAudioStreamReceiver); }
附:
通過物理鍵或者線控來控制音訊播放
標識要使用的音訊流
建立可預測的音訊體驗的第一步是瞭解您的應用將使用哪個音訊流。
Android維護一個單獨的音訊流,用於播放音樂,鬧鐘,通知,來電鈴聲,系統聲音,通話音量和DTMF鈴聲。 這主要是為了允許使用者獨立地控制每個流的音量。
大多數這些流僅限於系統事件,因此除非您的應用程式是替換鬧鐘,否則您幾乎肯定會使用STREAM_MUSIC流播放您的音訊。
預設情況下,按音量控制可修改Activity音訊流的音量。如果您的應用程式目前沒有播放任何內容,按下音量鍵即可調整鈴聲音量。
如果你有一個遊戲或音樂應用程式,那麼當用戶點選音量鍵,他們想要控制遊戲或音樂的音量,即使他們當前在歌曲之間或沒有音樂在當前遊戲位置。
你可能會試著嘗試監聽音量鍵按下並修改音訊流的音量。不要慌, Android提供了方便的setVolumeControlStream()方法來將音量鍵降到您指定的音訊流。
確定應用程式將使用的音訊流後,應將其設定為音訊流目標。您應該在應用程式的生命週期中提前進行此呼叫 - 因為您只需在Activity生命週期中呼叫一次,通常應在onCreate()方法(控制您的媒體的Activity或Fragment)中呼叫它。這確保了只要應用程式可見,音量控制功能就像使用者期望的那樣。
setVolumeControlStream(AudioManager.STREAM_MUSIC);
1
從這一點開始,只要目標Activity或Fragment可見,按裝置上的音量鍵就會影響您指定的音訊流;
媒體播放按鈕,如播放,暫停,停止,跳過和上一個在某些手機和許多連線或無線耳機可用。每當使用者按下這些硬體鍵之一時,系統使用ACTION_MEDIA_BUTTON操作廣播一個Intent。
要響應媒體按鈕點選,您需要在您的清單中註冊一個BroadcastReceiver,監聽此操作廣播,如下所示。
<receiver android:name=".RemoteControlReceiver"> <intent-filter> <action android:name="android.intent.action.MEDIA_BUTTON" /> </intent-filter> </receiver>
接收器實現本身需要提取哪個鍵被按下來引起廣播。 Intent在EXTRA_KEY_EVENT鍵下包括此鍵,而KeyEvent類包括表示每個可能的媒體按鈕的列表KEYCODE_MEDIA_ *靜態常量,例如KEYCODE_MEDIA_PLAY_PAUSE和KEYCODE_MEDIA_NEXT。
以下程式碼片段顯示如何提取按下的媒體按鈕,並相應地影響媒體播放:
public class RemoteControlReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { if (Intent.ACTION_MEDIA_BUTTON.equals(intent.getAction())) { KeyEvent event = (KeyEvent)intent.getParcelableExtra(Intent.EXTRA_KEY_EVENT); if (KeyEvent.KEYCODE_MEDIA_PLAY == event.getKeyCode()) { // Handle key press. } } } }
因為多個應用程式可能想要監聽媒體按鈕按下,所以您還必須以程式設計方式控制應用程式何時應該接收媒體按鈕按下事件。
以下程式碼可在您的應用程式中使用AudioManager註冊和登出媒體按鈕事件接收器。註冊後,您的廣播接收器是所有媒體按鈕廣播的專屬接收器。
AudioManager am = mContext.getSystemService(Context.AUDIO_SERVICE); //... // Start listening for button presses am.registerMediaButtonEventReceiver(RemoteControlReceiver); //... // Stop listening for button presses am.unregisterMediaButtonEventReceiver(RemoteControlReceiver);
通常,當應用程式變為不活動或不可見時(例如在onStop()回撥期間),應取消註冊大多數接收者。然而,對於媒體播放應用程式來說並不簡單,事實上,當應用程式不可見且無法通過螢幕上的使用者介面控制時,響應媒體播放按鈕是最重要的。
---------------------
from:https://blog.csdn.net/zhaobo012387/article/details/75206268
概述在安卓開發中免不了需要播放一點音樂了,音訊了。但是這時候有別的應用正在播放,這時候就會出現重音的現象,完全影響使用者體驗,我們的專案就遇上了這樣的尷尬,然後查找了一些文件,記錄一下;
管理音訊焦點從谷歌開發瞭解到,這跟音訊的焦點又關係,我們可以獲取當前音訊的焦點來解決問題; 有多個應用程式可能播放音訊,重要的是要考慮他們應該如何互動。為了避免每個音樂應用程式同時播放,Android使用音訊焦點來控制音訊播放 - 只有擁有音訊焦點的應用程式才能播放音訊。 在您的應用程式開始播放音訊之前,應該請求並接收音訊焦點。同樣,它應該知道如何監聽音訊焦點的丟失,並在發生這種情況時適當地做出反應。
請求音訊焦點在應用開始播放任何音訊之前,自己的應用應該保留將使用的流的音訊焦點,簡單說就是我要現在的焦點,其他人靠邊,但是別人同樣也可能有這樣的操作,我們也需要監聽自己播放的焦點的變化,增加自己程式的相容性。這是通過呼叫requestAudioFocus()完成的,如果您的請求成功,它返回AUDIOFOCUS_REQUEST_GRANTED。
您必須指定您使用的流,以及是否需要暫時或永久的音訊焦點。當您希望只在短時間內播放音訊時請求暫時聚焦(例如在播放導航指示時)。當您計劃在可預見的未來播放音訊時請求永久音訊聚焦(例如,播放音樂時)
以下程式碼片段請求音樂音訊流的永久音訊焦點。您應該在開始播放之前立即請求音訊焦點,例如當用戶按下播放或下一個遊戲關卡的背景音樂開始時。
AudioManager am = mContext.getSystemService(Context.AUDIO_SERVICE);...
// Request audio focus for playbackint result = am.requestAudioFocus(afChangeListener,// Use the music stream.AudioManager.STREAM_MUSIC,// Request permanent focus.AudioManager.AUDIOFOCUS_GAIN);
if (result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {am.registerMediaButtonEventReceiver(RemoteControlReceiver);// Start playback.}1234567891011121314上述程式碼你就可以得到焦點播放你的音訊,但是一旦你完成播放,一定要呼叫abandonAudioFocus()。這會通知系統您不再需要焦點和登出相關聯的AudioManager.OnAudioFocusChangeListener。在放棄瞬態焦點的情況下,這允許任何中斷的應用程式繼續播放。
// Abandon audio focus when playback completeam.abandonAudioFocus(afChangeListener);12但是有些時候我們並不想完全停掉另一個程式的聲音,比如這時候正在進行高德導航,或者微信語音,希望另一個程式的聲音小點,不要蓋過自己即可,開車導航聽音樂的時候,我們應該遇到過;
當請求瞬態音訊焦點時,您有一個附加選項:是否要啟用“低音”。通常,當良好的音訊應用程式失去音訊焦點時,它立即使其播放靜音。通過請求一個允許迴避的暫時音訊焦點,你告訴其他音訊應用程式它們可以接受他們繼續播放,只要他們降低音量,直到焦點回到他們。
// Request audio focus for playbackint result = am.requestAudioFocus(afChangeListener,// Use the music stream.AudioManager.STREAM_MUSIC,// Request permanent focus.AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK);
if (result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {// Start playback.}12345678910每當另一個應用程式請求音訊焦點如上所述,其永久和瞬態(有或沒有支援低音)之間的選擇音訊焦點由您在請求焦點時註冊的偵聽器接收。
處理音訊焦點的丟失如果您的應用程式可以請求音訊焦點,那麼當其他應用程式請求焦點時,它會反過來失去焦點。您的應用程式如何響應音訊焦點的丟失取決於丟失的方式。
您請求音訊焦點時註冊的音訊焦點更改偵聽器的onAudioFocusChange()回撥方法接收到描述焦點更改事件的引數。具體來說,可能的聚焦丟失事件映象來自前一部分的聚焦請求型別 - 永久丟失,瞬時丟失和允許具有迴避的瞬態。
一般來說,暫時的(臨時的)音訊焦點的丟失應導致您的應用程式使它的音訊流靜音,否則保持相同的狀態。您應該繼續監視音訊焦點的變化,並準備在恢復焦點後暫停播放。
如果音訊焦點丟失是永久性的,假設另一個應用程式現在正用於聽音訊,您的應用程式應該有效地結束自己。實際上,這意味著停止播放,刪除媒體按鈕偵聽器 - 允許新的音訊播放器專門處理這些事件 - 並放棄您的音訊焦點。此時,您需要在恢復播放音訊之前需要執行使用者操作(在應用程式中按下播放)。
在下面的程式碼片段中,如果音訊丟失是暫時的,我們暫停播放我們的媒體播放器物件,並在我們恢復焦點後重新開始播放。如果丟失是永久性的,它將取消註冊我們的媒體按鈕事件接收器,並停止監視音訊焦點更改。
AudioManager.OnAudioFocusChangeListener afChangeListener =new AudioManager.OnAudioFocusChangeListener() {public void onAudioFocusChange(int focusChange) {if (focusChange == AUDIOFOCUS_LOSS_TRANSIENT) {// Pause playback} else if (focusChange == AudioManager.AUDIOFOCUS_GAIN) {// Resume playback} else if (focusChange == AudioManager.AUDIOFOCUS_LOSS) {am.unregisterMediaButtonEventReceiver(RemoteControlReceiver);am.abandonAudioFocus(afChangeListener);// Stop playback}}};1234567891011121314處理音訊焦點瞬間丟失在瞬時丟失音訊焦點的情況下,允許duck(迴避),而不是暫停回放。
OnAudioFocusChangeListener afChangeListener = new OnAudioFocusChangeListener() {public void onAudioFocusChange(int focusChange) {if (focusChange == AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK) {// Lower the volume} else if (focusChange == AudioManager.AUDIOFOCUS_GAIN) {// Raise it back to normal}}};123456789至此,我們就可以隨心所欲的處理我們音訊播放的焦點問題了
處理音訊輸出硬體當用戶從他們的Android裝置享受音訊時,使用者有很多選擇。大多數裝置都有內建揚聲器,有線耳機的耳機插孔,許多裝置還具有藍芽連線和支援A2DP音訊。
您的應用程式的行為可能會受到輸出路由到的硬體的影響。
您可以查詢AudioManager以確定音訊當前是否路由到裝置揚聲器,有線耳機或連線的藍芽裝置,如以下程式碼段所示:
if (isBluetoothA2dpOn()) {// Adjust output for Bluetooth.} else if (isSpeakerphoneOn()) {// Adjust output for Speakerphone.} else if (isWiredHeadsetOn()) {// Adjust output for headsets} else {// If audio plays and noone can hear it, is it still playing?}123456789處理音訊輸出硬體的更改
現在各種的音樂播放器都支援在拔掉耳機或者藍芽的情況正在播放的音視訊暫停,這是個很實用的功能,當然你也有必要支援一下;
當耳機拔掉或藍芽裝置斷開連線時,音訊流自動重新路由到內建揚聲器。如果你聽到你的音樂高達我的音量,這可能是一個嘈雜的驚喜。
幸運的是,當發生這種情況時,系統廣播ACTION_AUDIO_BECOMING_NOISY Intent。這是一個好的習慣,註冊一個BroadcastReceiver監聽這個Intent,無論你在播放音訊。在音樂播放器的情況下,使用者通常期望暫停播放,而對於遊戲,可以選擇顯著降低音量。
private class NoisyAudioStreamReceiver extends BroadcastReceiver {@Overridepublic void onReceive(Context context, Intent intent) {if (AudioManager.ACTION_AUDIO_BECOMING_NOISY.equals(intent.getAction())) {// Pause the playback}}}
private IntentFilter intentFilter = new IntentFilter(AudioManager.ACTION_AUDIO_BECOMING_NOISY);
private void startPlayback() {registerReceiver(myNoisyAudioStreamReceiver(), intentFilter);}
private void stopPlayback() {unregisterReceiver(myNoisyAudioStreamReceiver);}123456789101112131415161718附:通過物理鍵或者線控來控制音訊播放標識要使用的音訊流
建立可預測的音訊體驗的第一步是瞭解您的應用將使用哪個音訊流。
Android維護一個單獨的音訊流,用於播放音樂,鬧鐘,通知,來電鈴聲,系統聲音,通話音量和DTMF鈴聲。 這主要是為了允許使用者獨立地控制每個流的音量。
大多數這些流僅限於系統事件,因此除非您的應用程式是替換鬧鐘,否則您幾乎肯定會使用STREAM_MUSIC流播放您的音訊。
預設情況下,按音量控制可修改Activity音訊流的音量。如果您的應用程式目前沒有播放任何內容,按下音量鍵即可調整鈴聲音量。
如果你有一個遊戲或音樂應用程式,那麼當用戶點選音量鍵,他們想要控制遊戲或音樂的音量,即使他們當前在歌曲之間或沒有音樂在當前遊戲位置。
你可能會試著嘗試監聽音量鍵按下並修改音訊流的音量。不要慌, Android提供了方便的setVolumeControlStream()方法來將音量鍵降到您指定的音訊流。
確定應用程式將使用的音訊流後,應將其設定為音訊流目標。您應該在應用程式的生命週期中提前進行此呼叫 - 因為您只需在Activity生命週期中呼叫一次,通常應在onCreate()方法(控制您的媒體的Activity或Fragment)中呼叫它。這確保了只要應用程式可見,音量控制功能就像使用者期望的那樣。
setVolumeControlStream(AudioManager.STREAM_MUSIC);1從這一點開始,只要目標Activity或Fragment可見,按裝置上的音量鍵就會影響您指定的音訊流;
媒體播放按鈕,如播放,暫停,停止,跳過和上一個在某些手機和許多連線或無線耳機可用。每當使用者按下這些硬體鍵之一時,系統使用ACTION_MEDIA_BUTTON操作廣播一個Intent。
要響應媒體按鈕點選,您需要在您的清單中註冊一個BroadcastReceiver,監聽此操作廣播,如下所示。
<receiver android:name=".RemoteControlReceiver"><intent-filter><action android:name="android.intent.action.MEDIA_BUTTON" /></intent-filter></receiver>12345接收器實現本身需要提取哪個鍵被按下來引起廣播。 Intent在EXTRA_KEY_EVENT鍵下包括此鍵,而KeyEvent類包括表示每個可能的媒體按鈕的列表KEYCODE_MEDIA_ *靜態常量,例如KEYCODE_MEDIA_PLAY_PAUSE和KEYCODE_MEDIA_NEXT。
以下程式碼片段顯示如何提取按下的媒體按鈕,並相應地影響媒體播放:
public class RemoteControlReceiver extends BroadcastReceiver {@Overridepublic void onReceive(Context context, Intent intent) {if (Intent.ACTION_MEDIA_BUTTON.equals(intent.getAction())) {KeyEvent event = (KeyEvent)intent.getParcelableExtra(Intent.EXTRA_KEY_EVENT);if (KeyEvent.KEYCODE_MEDIA_PLAY == event.getKeyCode()) {// Handle key press.}}}}1234567891011因為多個應用程式可能想要監聽媒體按鈕按下,所以您還必須以程式設計方式控制應用程式何時應該接收媒體按鈕按下事件。
以下程式碼可在您的應用程式中使用AudioManager註冊和登出媒體按鈕事件接收器。註冊後,您的廣播接收器是所有媒體按鈕廣播的專屬接收器。
AudioManager am = mContext.getSystemService(Context.AUDIO_SERVICE);//...
// Start listening for button pressesam.registerMediaButtonEventReceiver(RemoteControlReceiver);//...
// Stop listening for button pressesam.unregisterMediaButtonEventReceiver(RemoteControlReceiver);123456789通常,當應用程式變為不活動或不可見時(例如在onStop()回撥期間),應取消註冊大多數接收者。然而,對於媒體播放應用程式來說並不簡單,事實上,當應用程式不可見且無法通過螢幕上的使用者介面控制時,響應媒體播放按鈕是最重要的。--------------------- 作者:zhaobo012387 來源:CSDN 原文:https://blog.csdn.net/zhaobo012387/article/details/75206268 版權宣告:本文為博主原創文章,轉載請附上博文連結!