若是您不妥善处理好音频聚焦,您的用户可能受到下图所示的困扰。
前端
开始代码示例以前,先看看下图,它展现了实现步骤: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、前端、后端、产品、设计 等领域,想要查看更多优质译文请持续关注 掘金翻译计划、官方微博、知乎专栏。