Android是多任务系统,Audio系统是竞争资源,Android2.2之前,没有内建的机制来解决多个程序竞争Audio的问题,2.2引入了称作AudioFocus的机制来管理对Audio资源的竞争的管理与协调。
未解决以上问题,可以使用AudioFocus的机制,即是在使用AudioStream之前,需要申请AudioFocus,在获得AudioFocus之后才可以使用相应的AudioStream;如果有别的程序竞争你正在使用的AudioStream,你的程序需要在收到通知之后做停止播放或者降低声音的处理。
注意
!!如果不做音频焦点监听操作,会出现多个音频同时播放的问题,所有应用都应当遵守音频焦点使用规则,获取到焦点的应用才去播放音频,未获取到或者丢失焦点应当暂停播放!!(其实Android没有强制每个App必须去遵守AudioFocus的使用规范,可能会出现有几个App同时发出声音的情况。但是大多数App都需要自动去那样做,需要的时候请求,用完的时候释放,这样才能保证有良好的使用体验。)
在项目中播放音频一般都是在service中处理播放的逻辑,直接在onCreate中请求AudioFocus,监听播放状态,销毁服务时记得移除监听
@Override
public void onCreate() {
audioManager = (AudioManager) getApplicationContext().getSystemService(Context.AUDIO_SERVICE);
mListener = new MyOnAudioFocusChangeListener();
// 请求AudioFocus,注册监听
int result = audioManager.requestAudioFocus(mListener,
AudioManager.STREAM_MUSIC,
AUDIOFOCUS_GAIN);
if (result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
//能打印出这句话标识已监听成功
Log.e("MyOnAudioFocus", "requestAudioFocus successfully.");
} else {
Log.e("MyOnAudioFocus", "requestAudioFocus failed.");
}
//...此处省略其他代码
}
@Override
public void onDestory(){
//...此处省略其他代码
//移除监听
audioManager.abandonAudioFocus(mListener);
}
具体的对监听的结果进行相应的处理,只需要实现相应的类即可,监听的状态有如下四种
AUDIOFOCUS_GAIN:当前应用获得了Audio Focus;
AUDIOFOCUS_LOSS:当前应用失去了Audio Focus,并将会持续很长的时间。这里因为可能会停掉很长时间,所以不仅仅要停止Audio的播放,最好直接释放掉Media资源。而因为停止播放Audio的时间会很长,如果程序因为这个原因而失去AudioFocus,最好不要让它再次自动获得AudioFocus而继续播放,不然突然冒出来的声音会让用户感觉莫名其妙,感受很不好。这里直接放弃AudioFocus,当然也不用再侦听远程播放控制【如下面代码的处理】。要再次播放,除非用户再在界面上点击开始播放,才重新初始化Media,进行播放。
AUDIOFOCUS_LOSS_TRANSIENT:当前应用暂时失去Audio Focus,并会很快再次获得。必须停止Audio的播放,但是因为可能会很快再次获得AudioFocus,这里可以不释放Media资源;
AUDIO重点内容FOCUS_LOSS_TRANSIENT_CAN_DUCK:当前应用暂时失去AudioFocus,但是可以继续播放,不过要在降低音量。
在项目中我对 AUDIOFOCUS_LOSS_TRANSIENT AUDIOFOCUS_LOSS_TRANSIENT这两种情况作了相同的处理,都是进行了播放的暂停。isUserPauseAudio这是一个我自己定义的标志位,用来记录是否是用户手动的暂停音乐的播放,如果是用户手动的暂停音乐的播放,那么即使获取到了AudioFocus也不会进行相应的操作,这一点很重要。
private class MyOnAudioFocusChangeListener implements AudioManager.OnAudioFocusChangeListener {
@Override
public void onAudioFocusChange(int focusChange) {
//监听系统播放状态的改变
//Log.e("MyOnAudioFocus", "focusChange=" + focusChange);
case AUDIOFOCUS_REQUEST_GRANTED:
//重新得到焦点
break;
case AUDIOFOCUS_REQUEST_FAILED:
//申请焦点失败
break;
case AUDIOFOCUS_LOSS_TRANSIENT://短时间失去焦点
break;
case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:
//暂时失去AudioFocus,但是可以继续播放,不过要在降低音量。
break;
case AUDIOFOCUS_LOSS:
//失去焦点并将会持续很长的时间这里因为可能会停掉很长时间,所以不仅仅要停止Audio的播放,最好直接释放掉Media资源。而因为停止播放Audio的时间会很长,如果程序因为这个原因而失去AudioFocus,最好不要让它再次自动获得AudioFocus而继续播放,不然突然冒出来的声音会让用户感觉莫名其妙,感受很不好。这里直接放弃AudioFocus,当然也不用再侦听远程播放控制【如下面代码的处理】。要再次播放,除非用户再在界面上点击开始播放,才重新初始化Media,进行播放。
break;
}
}
另:当系统闹铃响起或者收到手机来电时,会收到AUDIOFOCUS_LOSS_TRANSIENT的回调,短时间失去焦点,但是闹铃响时,app可以抢占音频资源,继续播放自己的声音,闹铃会静音;但是手机来电时,无法抢占音频资源,无法被静音,且未接听之前无法播放音频
最后分析一下QQ微信语音消息以及拨打语音电话的时候AudioFocus的变化情况,以及相应的处理方式。
操作 状态 处理方式
录制语音消息/播放语音消息 AUDIOFOCUS_LOSS_TRANSIENT 暂停播放,录制/播放结束后,会重新获取AudioFocus,继续播放
被动接收QQ/微信电话 AUDIOFOCUS_LOSS_TRANSIENT 同上
主动拨打QQ/微信电话 & 使用第三方的播放器听歌(酷狗、QQ音乐等) AUDIOFOCUS_LOSS 停止播放,拨打电话后,不能再获取到焦点,不再继续播放
一些参数:
- streamType是《Android中的Audio播放:音量和远程播放控制》中说明的AudioStream,其值取决于AudioManager中的STREAM_xxx,在AudioStream的裁决机制中并未有什么实际意义;
-
durationHint是持续性的指示:
- AUDIOFOCUS_GAIN指示申请得到的Audio Focus不知道会持续多久,一般是长期占有;
- AUDIOFOCUS_GAIN_TRANSIENT指示要申请的AudioFocus是暂时性的,会很快用完释放的;
- AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK不但说要申请的AudioFocus是暂时性的,还指示当前正在使用AudioFocus的可以继续播放,只是要“duck”一下(降低音量)。
- AudioManager.OnAudioFocusChangeListener是申请成功之后监听AudioFocus使用情况的Listener,后续如果有别的程序要竞争AudioFocus,都是通过这个Listener的onAudioFocusChange()方法来通知这个Audio Focus的使用者的。