音频焦点总结
参考很多大神们的资料,再次表示感谢,
什么叫音频焦点 (audio focus)?
android系统是一个多任务操作系统,因此同一时刻允许许多任务同时工作。但是这对音频类应用来说是个挑战,因为如果多个音频同时播放的话,很多情况下用户体验会相当的差!比如听音乐时,来了个电话,这时你的耳机里就是电话和音乐共同工作,绝对是个悲剧!
为了解决这个问题从android2.2开始引入audio focus的概念。为避免多个音乐 App 在同时请求音频播放的时候发生冲突,Android 平台使用了音频焦点这一概念来协调音频播放——即只有获得音频焦点的 App 才可以播放音频。
AudioFocus是一个没有优先级概念的抢占式的机制。在一般情况下后一个申请者都能从前一个申请者的手中获取AudioFocus。不过只有一个例外,就是通话。通话作为手机的首要功能,同时也是一种音频的播放过程,所以从来电铃声开始到通话结束这个过程,Telephony相关的模块也会申请AudioFocus,但是它的优先级是最高的。Telephony可以从所有人手中抢走AudioFocus,但是任何人无法从它手中将其夺回。
值得一提的是,AudioFocus机制完全是一个建议性而不是强制性的机制。也就是说,上述的行为是建议回放实例遵守,而不是强制的。所以,市面上仍有一些带有音频播放功能的应用没有采用这套机制。
在应用开始播放音频之前,它需要首先请求并获得音频焦点。同时,App 应该能监听失去音频焦点事件,并做出相应的处理。
开始播放前必须要获取需要处理的音频流的音频焦点。实现音频焦点监听器OnAudioFocusChangeListener,这个监听器会根据当前音频焦点的变化,调用onAudioFocusChange(int focusChange)方法,focusChange主要有以下四种参数:
1、AUDIOFOCUS_GAIN:你已经获得音频焦点;
2、AUDIOFOCUS_LOSS:你已经失去音频焦点很长时间了,必须终止所有的音频播放。因为长时间的失去焦点后,不应该在期望有焦点返回,这是一个尽可能清除不用资源的好位置。例如,应该在此时释放MediaPlayer对象;
3、AUDIOFOCUS_LOSS_TRANSIENT:这说明你临时失去了音频焦点,但是在不久就会再返回来。此时,你必须终止所有的音频播放,但是保留你的播放资源,因为可能不久就会返回来。
4、AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:这说明你已经临时失去了音频焦点,但允许你安静的播放音频(低音量),而不是完全的终止音频播放。目前所有的情况下,onFocusChange的时候停止mediaPlayer
获取方法: requestAudioFocus()方法获得
获取成功:音频焦点成功获取后,该方法会返回 AUDIOFOCUS_REQUEST_GRANTED常量否则,会返回 AUDIOFOCUS_REQUEST_FAILED 常量。
需要指定进行操作的音频流,并确定要获取短暂的还是长期的音频焦点。短暂的音频焦点用于处理播放短时间的音频(例如汽车导航仪的提示)。如果您想长时间播放音频(例如播放音乐),那么就需要请求长期的音频焦点。
获得音频焦点的请求应该在马上就要播放音频前发出,比如在用户按下播放键或下一关游戏的背景音乐就要开始时发出焦点请求,接着再播放音乐。
AudioManager am = mContext.getSystemService( Context.AUDIO_SERVICE);
//请求播放的音频焦点
int result = am.requestAudioFocus(afChangeListener,
// 指定所使用的音频流
AudioManager.STREAM_MUSIC,
// 请求长时间的音频焦点
AudioManager.AUDIOFOCUS_GAIN);
if(resul==AudioManager.AUDIOFOCUS_REQUEST_GRANTED){
am.unregisterMediaButtonEventReceiver( RemoteControlReceiver;
//开始播放
}
当完成音频播放后,请一定记得调用 abandonAudioFocus()方法,这会通知系统您的App不再需要音频焦点,并移除相关 OnAudioFocusChangeListener 的注册。如果释放的是短暂音调焦点,那么被打断的音频会被继续播放。
// 当播放结束,您需要释放音频焦点
am.abandonAudioFocus(afChangeListener);
当请求短暂音频焦点时,您可以添加有一额外的选择——是否使用“浮动声音(英文为“ducking”,这里是指降低原音频流播放的音量,并使获得短暂音频焦点的音频流音量较大,而不去停止原来音频流的播放)”方式。
通常来说,一个好的音频播放App 会在失去音频焦点时立即停止播放。但如果在请求短暂音频焦点时使用“浮动声音”方式,可以允许先前的App以较低的音量继续播放,在重新获得音频焦点后再以原来的音量播放。
// 请求播放的音频焦点
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 失去短暂的音频焦点时会暂停播放,当重新获得焦点时会继续播放。当失去的是长期音频焦点时,就会取消媒体按键事件接收器的注册并停止对音频焦点变化的监听。
OnAudioFocusChangeListener am = 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)的音频焦点,相比暂停播放,更好的做法应该是使用“浮动播放”的方式。
浮动播放( Duck)
浮动播放会将您正使用的音频流输出音量降低,这会使其他 App 的短暂音频更容易听到,如此一来您的 App 就不用被完全打断了。
下面的代码会使 App 在暂时失去焦点时降低媒体播放器的音量,并在重新获得音频焦点时恢复到原来的音量大小。
OnAudioFocusChangeListeneam=new OnAudioFocusChangeListener(){
public void onAudioFocusChange( int focusChange) {
if(focusChange== AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK) {
// 降低音量
} else if (focusChange == AudioManager.AUDIOFOCUS_GAIN) {
// 恢复至正常音量
}
}
};
失去音频焦点的广播是需要响应的重要广播之一。系统会广播一系列 intent 对象来提醒您需要对用户的音频体验进行调整。
使用Audio的程序要做到:
使用前,用requestAudioFocus()申请AudioFocus,并根据应用的实际选取恰当的durationHint值;正确的在AudioManager.OnAudioFocusChangeListener中响应AudioFocus失去和重新获取事件;Audio使用结束,用abandonAudioFocus()归还AudioFocus。