若是您不妥善處理好音頻聚焦,您的用戶可能受到下圖所示的困擾。
前端
開始代碼示例以前,先看看下圖,它展現了實現步驟:android
獲取音頻焦點的第一個步驟是先向系統發出申請焦點的消息。注意這只是發出請求,並不是直接獲取。爲了申請到音頻聚焦,您必須向系統描述好您的意圖。介紹四個常見音頻焦點類型:git
在 Android O 或者更新的版本上您必須使用 builder 來實例化一個 AudioFocusRequest 類。(在 builder 中必須指明請求的音頻焦點類型)github
AudioManager mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
AudioAttributes mAudioAttributes =
new AudioAttributes.Builder()
.setUsage(AudioAttributes.USAGE_MEDIA)
.setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
.build();
AudioFocusRequest mAudioFocusRequest =
new AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN)
.setAudioAttributes(mAudioAttributes)
.setAcceptsDelayedFocusGain(true)
.setOnAudioFocusChangeListener(...) // Need to implement listener
.build();
int focusRequest = mAudioManager.requestAudioFocus(mAudioFocusRequest);
switch (focusRequest) {
case AudioManager.AUDIOFOCUS_REQUEST_FAILED:
// 不容許播放
case AudioManager.AUDIOFOCUS_REQUEST_GRANTED:
// 開始播放
}複製代碼
音頻焦點類型要點:後端
在 Android N 及其更早的版本中,不須要用到 AudioFocusRequest,只需實現 AudioManager.OnAudioFocusChangeListener 接口。代碼以下:bash
AudioManager mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
int focusRequest = mAudioManager.requestAudioFocus(
..., // Need to implement listener
AudioManager.STREAM_MUSIC,
AudioManager.AUDIOFOCUS_GAIN);
switch (focusRequest) {
case AudioManager.AUDIOFOCUS_REQUEST_FAILED:
// don't start playback case AudioManager.AUDIOFOCUS_REQUEST_GRANTED: // actually start playback }複製代碼
上述皆爲音頻焦點的申請,接下來咱們將介紹 AudioManager.OnAudioFocusChangeListener 如何實現,以此來響應音頻焦點的狀態。微信
一旦得到音頻聚焦,您的應用要立刻作出響應,由於它的狀態可能在任什麼時候間發生改變(丟失或從新獲取),您能夠實現 OnAudioFocusChangeListener 的來響應狀態改變。app
如下代碼展現了 OnAudioFocusChangeListener 接口的實現,它處理了與 Google Assistant 應用協同工做的時候,音頻焦點的各類狀態的變化。ide
private final class AudioFocusHelper
implements AudioManager.OnAudioFocusChangeListener {
private void abandonAudioFocus() {
mAudioManager.abandonAudioFocus(this);
}
@Override
public void onAudioFocusChange(int focusChange) {
switch (focusChange) {
case AudioManager.AUDIOFOCUS_GAIN:
if (mPlayOnAudioFocus && !isPlaying()) {
play();
} else if (isPlaying()) {
setVolume(MEDIA_VOLUME_DEFAULT);
}
mPlayOnAudioFocus = false;
break;
case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:
setVolume(MEDIA_VOLUME_DUCK);
break;
case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
if (isPlaying()) {
mPlayOnAudioFocus = true;
pause();
}
break;
case AudioManager.AUDIOFOCUS_LOSS:
mAudioManager.abandonAudioFocus(this);
mPlayOnAudioFocus = false;
stop();
break;
}
}
}複製代碼
關於暫停播放,應用程序的行爲應該是不一樣的。若是用戶主動暫停播放時,您的應用應釋放音頻焦點。若是是爲了響應音頻焦點的暫時丟失而暫停播放,則不該釋放音頻焦點。 這裏有一些用例來講明這一點。工具
分析上面接口mPlayOnAudioFocus 的場景,您的音頻應用正在後臺播放音樂:
如下代碼展現如何釋放音頻焦點:
public final void pause() {
if (!mPlayOnAudioFocus) {
mAudioFocusHelper.abandonAudioFocus();
}
onPause();
}複製代碼
您能夠看到釋放焦點是在用戶暫停播放的時候,而非其它應用請求焦點 AUDIOFOCUS_GAIN_TRANSIENT 致使他們釋放焦點。
應對焦點丟失
選擇在 OnAudioFocusChangeListener 中暫停仍是下降音量,取決於您應用的交互方式。在 Android O上,會自動的幫您下降音量,因此您能夠忽略 OnAudioFocusChangeListener 接口的 AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK 事件。
在 Android O 如下的版本,您須要本身用代碼實現,具體實現方式如上面代碼所示。
延遲聚焦
Android O 介紹了延遲聚焦這個概念,您能夠在申請音頻聚焦的時候來響應 AUDIOFOCUS_REQUEST_DELAYED 這個結果,以下所示:
public void requestPlayback() {
int audioFocus = mAudioManager.requestAudioFocus(mAudioFocusRequest);
switch (audioFocus) {
case AudioManager.AUDIOFOCUS_REQUEST_FAILED:
...
case AudioManager.AUDIOFOCUS_REQUEST_GRANTED:
...
case AudioManager.AUDIOFOCUS_REQUEST_DELAYED:
mAudioFocusPlaybackDelayed = true;
}
}複製代碼
在您 OnAudioFocusChangeListener 的實現,您須要檢查 mAudioFocusPlaybackDelayed 這個變量,當您響應 AUDIOFOCUS_GAIN 音頻聚焦的時候, 以下所示:
private void onAudioFocusChange(int focusChange) {
switch (focusChange) {
case AudioManager.AUDIOFOCUS_GAIN:
logToUI("Audio Focus: Gained");
if (mAudioFocusPlaybackDelayed || mAudioFocusResumeOnFocusGained) {
mAudioFocusPlaybackDelayed = false;
mAudioFocusResumeOnFocusGained = false;
start();
}
break;
case AudioManager.AUDIOFOCUS_LOSS:
mAudioFocusResumeOnFocusGained = false;
mAudioFocusPlaybackDelayed = false;
stop();
break;
case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
mAudioFocusResumeOnFocusGained = true;
mAudioFocusPlaybackDelayed = false;
pause();
break;
case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:
pause();
break;
}
}複製代碼
播放完音頻,記得使用 AudioManager.abandonAudioFocus(…) 來釋放掉音頻焦點。在前面的步驟中,咱們遇到了一個應用暫停播放應該釋放音頻焦點的狀況,可是這個應用依舊保留了音頻焦點。
幾個您能夠在您應用使用的案例
在 GitHub gist 上有三個類關於音頻焦點的使用,這可能對您的代碼有幫助。
完整的代碼示例
android-MediaBrowserService 完整展現了音頻焦點的處理,使用 MediaPlayer 來播放音樂,同時使用了 MediaSession 。
PlayerAdapter展現了音頻聚焦的最佳實踐,請注意 pause() 和 onAudioFocusChange(int) 方法的實現。
一旦您在應用中實現了音頻焦點的處理,您可使用安卓媒體控制工具來測試您的應用對音頻聚焦的真實反映,具體使用方法請查閱 GitHub/Android Media Controller.
掘金翻譯計劃 是一個翻譯優質互聯網技術文章的社區,文章來源爲 掘金 上的英文分享文章。內容覆蓋 Android、iOS、React、前端、後端、產品、設計 等領域,想要查看更多優質譯文請持續關注 掘金翻譯計劃、官方微博、知乎專欄。