原文链接:http://docs.eoeandroid.com/training/managing-audio/audio-focus.html
作者:GloriousOnion
完成时间:
由于存在多个App同时请求播放音频的情况,所以您需要考虑这些App该如何交互。为避免多个音乐App在同时请求音频播放的时候发生冲突,Android平台使用了音频焦点这一概念来协调音频播放——即只有获得音频焦点的App才可以播放音频。
在应用开始播放音频之前,它需要首先请求并获得音频焦点。同时,App应该能监听失去音频焦点事件,并做出相应的处理。
请求音频焦点
在开始播放音频之前,App必须先获取需要处理的音频流的音频焦点。音频焦点可以通过requestAudioFocus()方法获得,在音频焦点成功获取后,该方法会返回AUDIOFOCUS_REQUEST_GRANTED常量,否则,会返回AUDIOFOCUS_REQUEST_FAILED常量。
您需要指定进行操作的音频流,并确定要获取短暂的还是长期的音频焦点。短暂的音频焦点用于处理播放短时间的音频(例如汽车导航仪的提示)。如果您想长时间播放音频(例如播放音乐),那么就需要请求长期的音频焦点。
下面的代码演示了如何获取Music音频流的长期焦点。获得音频焦点的请求应该在马上就要播放音频前发出,比如在用户按下播放键或下一关游戏的背景音乐就要开始时发出焦点请求,接着再播放音乐。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
AudioManager am = mContext.getSystemService(Context.AUDIO_SERVICE);
...
//请求播放的音频焦点
int result = am.requestAudioFocus(afChangeListener,
// 指定所使用的音频流
AudioManager.STREAM_MUSIC,
// 请求长时间的音频焦点
AudioManager.AUDIOFOCUS_GAIN);
if (result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
am.unregisterMediaButtonEventReceiver(RemoteControlReceiver);
// 开始播放
}
|
当完成音频播放后,请一定记得调用abandonAudioFocus()方法,这会通知系统您的App不再需要音频焦点,并移除相关OnAudioFocusChangeListener的注册。如果释放的是短暂音调焦点,那么被打断的音频会被继续播放。
1 2 |
// 当播放结束,您需要释放音频焦点
am.abandonAudioFocus(afChangeListener);
|
当请求短暂音频焦点时,您可以添加有一额外的选择——是否使用“浮动声音(英文为“ducking”,这里是指降低原音频流播放的音量,并使获得短暂音频焦点的音频流音量较大,而不去停止原来音频流的播放)”方式。
通常来说,一个好的音频播放App会在失去音频焦点时立即停止播放。但如果在请求短暂音频焦点时使用“浮动声音”方式,可以允许先前的App以较低的音量继续播放,在重新获得音频焦点后再以原来的音量播放。
1 2 3 4 5 6 7 8 9 10 |
// 请求播放的音频焦点
int result = am.requestAudioFocus(afChangeListener,
// 指定所使用的音频流
AudioManager.STREAM_MUSIC,
// 请求短暂焦点
AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK);
if (result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
// 开始播放
}
|
浮动音频非常适合间断播放音频的App,例如导航仪的提示。
当其他App通过上述方式请求音频焦点时,您所注册的监听器可以判断是否获得到了长期或短暂(可以选择浮动音频方式)的音频焦点。
App请求并得到音频焦点后,当其他App请求得到焦点时,先前的App就会失去焦点。您的App需要根据失去音频焦点的不同方式进行相应处理。
请求音频焦点时注册的音频焦点监听器中有onAudioFocusChange(int)回调函数,该回调函数会接收描述焦点变化事件的参数。需要注意的是,失去音频焦点的事件类型与请求焦点的类型相对应——失去长期焦点(AUDIOFOCUS_LOSS)、短暂焦点(AUDIOFOCUS_LOSS_TRANSIENT)和浮动音频方式的短暂焦点(AUDIOFOCUS_LOSS_TRANSIENT)。
一般情况下,App在失去短暂音频焦点时,应该停止播放并记录下播放状态。但仍然需要监听音频焦点的变化,当重新获得音频焦点时,需要在从先前暂停的地方继续播放。
如果失去的是长期音频焦点,那就是说其他App需要使用当前音频流,而您的App需要尽快结束。实际上,意味着您的App需要停止播放,移除媒体键监听器并释放音频焦点——这将允许新的音频播放器独占地持有音频焦点等。这样一来,只有当用户执行了新的操作(如点击App中的播放按钮),才可以重新播放音频。
在下面的代码中,当App失去短暂的音频焦点时会暂停播放,当重新获得焦点时会继续播放。当失去的是长期音频焦点时,就会取消媒体按键事件接收器的注册并停止对音频焦点变化的监听。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
OnAudioFocusChangeListener afChangeListener = new OnAudioFocusChangeListener() {
public void onAudioFocusChange(int focusChange) {
if (focusChange == AUDIOFOCUS_LOSS_TRANSIENT
// 暂停播放
} else if (focusChange == AudioManager.AUDIOFOCUS_GAIN) {
// 恢复播放
} else if (focusChange == AudioManager.AUDIOFOCUS_LOSS) {
am.unregisterMediaButtonEventReceiver(RemoteControlReceiver);
am.abandonAudioFocus(afChangeListener);
// 停止播放
}
}
};
|
如果失去的是短暂且允许使用浮动播放(duck)的音频焦点,相比暂停播放,更好的做法应该是使用“浮动播放”的方式。
浮动播放会将您正使用的音频流输出音量降低,这会使其他App的短暂音频更容易听到,如此一来您的App就不用被完全打断了。
下面的代码会使App在暂时失去焦点时降低媒体播放器的音量,并在重新获得音频焦点时恢复到原来的音量大小。
1 2 3 4 5 6 7 8 9 |
OnAudioFocusChangeListener afChangeListener = new OnAudioFocusChangeListener() {
public void onAudioFocusChange(int focusChange) {
if (focusChange == AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK
// 降低音量
} else if (focusChange == AudioManager.AUDIOFOCUS_GAIN) {
// 恢复至正常音量
}
}
};
|
失去音频焦点的广播是需要响应的重要广播之一。系统会广播一系列intent对象来提醒您需要对用户的音频体验进行调整。在下一课中会介绍如何接收这些提醒,并从整体上提升用户体验。