Android是多任務(wù)系統(tǒng),Audio系統(tǒng)是競爭資源,Android2.2之前,沒有內(nèi)建的機(jī)制來解決多個程序競爭Audio的問題,2.2引入了稱作AudioFocus的機(jī)制來管理對Audio資源的競爭的管理與協(xié)調(diào)。
未解決以上問題,可以使用AudioFocus的機(jī)制,即是在使用AudioStream之前,需要申請AudioFocus,在獲得AudioFocus之后才可以使用相應(yīng)的AudioStream;如果有別的程序競爭你正在使用的AudioStream,你的程序需要在收到通知之后做停止播放或者降低聲音的處理。
注意
!!如果不做音頻焦點(diǎn)監(jiān)聽操作,會出現(xiàn)多個音頻同時播放的問題,所有應(yīng)用都應(yīng)當(dāng)遵守音頻焦點(diǎn)使用規(guī)則,獲取到焦點(diǎn)的應(yīng)用才去播放音頻,未獲取到或者丟失焦點(diǎn)應(yīng)當(dāng)暫停播放!!(其實(shí)Android沒有強(qiáng)制每個App必須去遵守AudioFocus的使用規(guī)范,可能會出現(xiàn)有幾個App同時發(fā)出聲音的情況。但是大多數(shù)App都需要自動去那樣做,需要的時候請求,用完的時候釋放,這樣才能保證有良好的使用體驗(yàn)。)
在項目中播放音頻一般都是在service中處理播放的邏輯,直接在onCreate中請求AudioFocus,監(jiān)聽播放狀態(tài),銷毀服務(wù)時記得移除監(jiān)聽
@Override
public void onCreate() {
audioManager = (AudioManager) getApplicationContext().getSystemService(Context.AUDIO_SERVICE);
mListener = new MyOnAudioFocusChangeListener();
// 請求AudioFocus,注冊監(jiān)聽
int result = audioManager.requestAudioFocus(mListener,
AudioManager.STREAM_MUSIC,
AUDIOFOCUS_GAIN);
if (result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
//能打印出這句話標(biāo)識已監(jiān)聽成功
Log.e("MyOnAudioFocus", "requestAudioFocus successfully.");
} else {
Log.e("MyOnAudioFocus", "requestAudioFocus failed.");
}
//...此處省略其他代碼
}
@Override
public void onDestory(){
//...此處省略其他代碼
//移除監(jiān)聽
audioManager.abandonAudioFocus(mListener);
}
具體的對監(jiān)聽的結(jié)果進(jìn)行相應(yīng)的處理,只需要實(shí)現(xiàn)相應(yīng)的類即可,監(jiān)聽的狀態(tài)有如下四種
AUDIOFOCUS_GAIN:當(dāng)前應(yīng)用獲得了Audio Focus;
AUDIOFOCUS_LOSS:當(dāng)前應(yīng)用失去了Audio Focus,并將會持續(xù)很長的時間。這里因?yàn)榭赡軙5艉荛L時間,所以不僅僅要停止Audio的播放,最好直接釋放掉Media資源。而因?yàn)橥V共シ臕udio的時間會很長,如果程序因?yàn)檫@個原因而失去AudioFocus,最好不要讓它再次自動獲得AudioFocus而繼續(xù)播放,不然突然冒出來的聲音會讓用戶感覺莫名其妙,感受很不好。這里直接放棄AudioFocus,當(dāng)然也不用再偵聽遠(yuǎn)程播放控制【如下面代碼的處理】。要再次播放,除非用戶再在界面上點(diǎn)擊開始播放,才重新初始化Media,進(jìn)行播放。
AUDIOFOCUS_LOSS_TRANSIENT:當(dāng)前應(yīng)用暫時失去Audio Focus,并會很快再次獲得。必須停止Audio的播放,但是因?yàn)榭赡軙芸煸俅潍@得AudioFocus,這里可以不釋放Media資源;
AUDIO重點(diǎn)內(nèi)容FOCUS_LOSS_TRANSIENT_CAN_DUCK:當(dāng)前應(yīng)用暫時失去AudioFocus,但是可以繼續(xù)播放,不過要在降低音量。
在項目中我對 AUDIOFOCUS_LOSS_TRANSIENT AUDIOFOCUS_LOSS_TRANSIENT這兩種情況作了相同的處理,都是進(jìn)行了播放的暫停。isUserPauseAudio這是一個我自己定義的標(biāo)志位,用來記錄是否是用戶手動的暫停音樂的播放,如果是用戶手動的暫停音樂的播放,那么即使獲取到了AudioFocus也不會進(jìn)行相應(yīng)的操作,這一點(diǎn)很重要。
private class MyOnAudioFocusChangeListener implements AudioManager.OnAudioFocusChangeListener {
@Override
public void onAudioFocusChange(int focusChange) {
//監(jiān)聽系統(tǒng)播放狀態(tài)的改變
//Log.e("MyOnAudioFocus", "focusChange=" + focusChange);
case AUDIOFOCUS_REQUEST_GRANTED:
//重新得到焦點(diǎn)
break;
case AUDIOFOCUS_REQUEST_FAILED:
//申請焦點(diǎn)失敗
break;
case AUDIOFOCUS_LOSS_TRANSIENT://短時間失去焦點(diǎn)
break;
case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:
//暫時失去AudioFocus,但是可以繼續(xù)播放,不過要在降低音量。
break;
case AUDIOFOCUS_LOSS:
//失去焦點(diǎn)并將會持續(xù)很長的時間這里因?yàn)榭赡軙5艉荛L時間,所以不僅僅要停止Audio的播放,最好直接釋放掉Media資源。而因?yàn)橥V共シ臕udio的時間會很長,如果程序因?yàn)檫@個原因而失去AudioFocus,最好不要讓它再次自動獲得AudioFocus而繼續(xù)播放,不然突然冒出來的聲音會讓用戶感覺莫名其妙,感受很不好。這里直接放棄AudioFocus,當(dāng)然也不用再偵聽遠(yuǎn)程播放控制【如下面代碼的處理】。要再次播放,除非用戶再在界面上點(diǎn)擊開始播放,才重新初始化Media,進(jìn)行播放。
break;
}
}
另:當(dāng)系統(tǒng)鬧鈴響起或者收到手機(jī)來電時,會收到AUDIOFOCUS_LOSS_TRANSIENT的回調(diào),短時間失去焦點(diǎn),但是鬧鈴響時,app可以搶占音頻資源,繼續(xù)播放自己的聲音,鬧鈴會靜音;但是手機(jī)來電時,無法搶占音頻資源,無法被靜音,且未接聽之前無法播放音頻
最后分析一下QQ微信語音消息以及撥打語音電話的時候AudioFocus的變化情況,以及相應(yīng)的處理方式。
操作 狀態(tài) 處理方式
錄制語音消息/播放語音消息 AUDIOFOCUS_LOSS_TRANSIENT 暫停播放,錄制/播放結(jié)束后,會重新獲取AudioFocus,繼續(xù)播放
被動接收QQ/微信電話 AUDIOFOCUS_LOSS_TRANSIENT 同上
主動撥打QQ/微信電話 & 使用第三方的播放器聽歌(酷狗、QQ音樂等) AUDIOFOCUS_LOSS 停止播放,撥打電話后,不能再獲取到焦點(diǎn),不再繼續(xù)播放
一些參數(shù):
- streamType是《Android中的Audio播放:音量和遠(yuǎn)程播放控制》中說明的AudioStream,其值取決于AudioManager中的STREAM_xxx,在AudioStream的裁決機(jī)制中并未有什么實(shí)際意義;
-
durationHint是持續(xù)性的指示:
- AUDIOFOCUS_GAIN指示申請得到的Audio Focus不知道會持續(xù)多久,一般是長期占有;
- AUDIOFOCUS_GAIN_TRANSIENT指示要申請的AudioFocus是暫時性的,會很快用完釋放的;
- AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK不但說要申請的AudioFocus是暫時性的,還指示當(dāng)前正在使用AudioFocus的可以繼續(xù)播放,只是要“duck”一下(降低音量)。
- AudioManager.OnAudioFocusChangeListener是申請成功之后監(jiān)聽AudioFocus使用情況的Listener,后續(xù)如果有別的程序要競爭AudioFocus,都是通過這個Listener的onAudioFocusChange()方法來通知這個Audio Focus的使用者的。