多个音源可以同时向同一个输出流进行播放音频,如果没有音频焦点管控,就会出现多个音源同时播放的现象,给用户带来不便;而Android为了避免多个音源同时播放,就引入了音频焦点的概念,所有音频应用都统一按照音频焦点的规定执行,就可以避免该现象发生。
当应用需要播放音频时,需要主动申请音频焦点,获取音频焦点后,再进行播放操作;同时在播放过程中,也可能存在其他音源请求焦点,此时当前应用就会收到音频焦点的丢失,当前应用应暂停播放或者降低音量,方便用户听其他音源。
- 在播放之前通过调用
requestAudioFocus
,确认返回的结果是否为AUDIOFOCUS_REQUEST_GRANTED
- 在其他应用获取到焦点时,停止或暂停播放,或降低音量
- 播放停止后,主动释放焦点
- 从
Android 2.2
开始,应用通过调用requestAudioFocus()
和abandonAudioFocus()
来管理音频焦点。应用还必须为这两个调用注册AudioManager.OnAudioFocusChangeListener
,以便接收回调并管理自己的音量。- 对于以
Android 5.0
及更高版本为目标平台的应用,音频应用应使用AudioAttributes
来描述应用正在播放的音频类型。例如,播放语音的应用应指定CONTENT_TYPE_SPEECH
。- 面向
Android 8.0
或更高版本的应用应使用requestAudioFocus()
方法,该方法会接受AudioFocusRequest
参数。AudioFocusRequest
包含有关应用的音频上下文和功能的信息。系统使用这些信息来自动管理音频焦点的得到和失去。
申请焦点时,需要明确焦点类型:
AudioManager.AUDIOFOCUS_GAIN
:表示应用程序需要长时间获得音频焦点,通常用于播放音乐或其他持续性的音频播放。应用程序在获得焦点后可以持续播放音频,直到明确放弃焦点或其他应用程序请求焦点。AudioManager.AUDIOFOCUS_GAIN_TRANSIENT
:表示应用程序需要短暂地获得音频焦点,通常用于播放短暂的音效或提示音。应用程序在获得焦点后可以播放音频,但在完成后应立即释放焦点,以便其他应用程序可以继续使用焦点。AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE
:表示应用程序需要短暂地获得音频焦点,并且希望其他应用程序不要同时播放音频。这种焦点类型用于应用程序需要短时间内独占音频焦点的情况,例如语音识别应用。AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK
:表示应用程序需要短暂地获得音频焦点,但愿意降低音频的音量以便与其他应用程序共享焦点。通常用于类似通知声音或导航提示的情况获取音频焦点结果,有以下类型:
AudioManager.AUDIOFOCUS_REQUEST_GRANTED
:表示应用程序成功获取了音频焦点。这意味着应用程序现在可以在其他应用程序不需要音频焦点时播放音频。AudioManager.AUDIOFOCUS_REQUEST_FAILED
:表示应用程序请求音频焦点失败。这可能是因为其他应用程序已经拥有了音频焦点,或者系统无法满足应用程序的请求。AudioManager.AUDIOFOCUS_REQUEST_DELAYED
:表示应用程序的音频焦点请求被暂时延迟。当其他应用程序释放音频焦点时,系统会尝试重新分配焦点给应用程序。在Android8.0
之前,需要通过AudioFocusRequest
,以及实现AudioManager.OnAudioFocusChangeListener
接口进行监听音频焦点变化
AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
AudioManager.OnAudioFocusChangeListener onAudioFocusChangeListener = new AudioManager.OnAudioFocusChangeListener() {
@Override
public void onAudioFocusChange(int state) {
switchFocus(state);
}
};
int requestAudioFocusResult = audioManager.requestAudioFocus(onAudioFocusChangeListener,
AudioManager.STREAM_MUSIC,
AudioManager.AUDIOFOCUS_GAIN);
if (requestAudioFocusResult == AudioManager.AUDIOFOCUS_REQUEST_FAILED) {
//请求焦点失败
} else if (requestAudioFocusResult == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
//请求焦点成功
}
public void switchFocus(int focus) {
switch (focus) {
case AudioManager.AUDIOFOCUS_GAIN:
break;
case AudioManager.AUDIOFOCUS_LOSS:
break;
case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
break;
case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:
break;
}
}
在Android8.0
之后,需要通过AudioFocusRequest
,AudioAttributes
,以及实现AudioManager.OnAudioFocusChangeListener
接口进行监听音频焦点变化
AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
AudioManager.OnAudioFocusChangeListener onAudioFocusChangeListener = new AudioManager.OnAudioFocusChangeListener() {
@Override
public void onAudioFocusChange(int state) {
switchFocus(state);
}
};
AudioAttributes attributes = new AudioAttributes.Builder()
.setUsage(AudioAttributes.USAGE_MEDIA)
.setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
.build();
AudioFocusRequest audioFocusRequest = new AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN)
.setAudioAttributes(attributes)
.setAcceptsDelayedFocusGain(true)
.setOnAudioFocusChangeListener(onAudioFocusChangeListener)
.setWillPauseWhenDucked(false)
.build();
int requestAudioFocusResult = audioManager.requestAudioFocus(audioFocusRequest);
if (requestAudioFocusResult == AudioManager.AUDIOFOCUS_REQUEST_FAILED) {
//请求焦点失败
} else if (requestAudioFocusResult == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
//请求焦点成功
} else if (requestAudioFocusResult == AudioManager.AUDIOFOCUS_REQUEST_DELAYED) {
//延时焦点
}
如上面所述,我们发现在Android 8.0之前,请求音频焦点的时候,只会返回两种结果,要么请求成功(AUDIOFOCUS_REQUEST_GRANTED
),要么请求失败(AUDIOFOCUS_REQUEST_FAILED
)。
而从Android 8.0开始,还有一种结果,延迟成功请求(AUDIOFOCUS_REQUEST_DELAYED
),这个也是成功的请求,但是这个请求具有延迟性;
方法 setAcceptsDelayedFocusGain(true)
可让您的应用异步处理焦点请求。设置此标记后,在焦点锁定时发出的请求会返回 AUDIOFOCUS_REQUEST_DELAYED
。当锁定音频焦点的情况不再存在时(例如当通话结束时),系统会批准待处理的焦点请求,通过注册AudioFocusChangeListener
监听的 onAudioFocusChange()
来通知您的应用。
在 Android 8.0(API 级别 26)中,当其他应用使用 AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK
请求焦点时,系统可以在不调用应用的 onAudioFocusChange()
回调的情况下降低和恢复音量。
在某些情境下,可能不需要降低音量而是直接暂停,这是可以通过setWillPauseWhenDucked(true)
方法取消系统的默认行为,然后通过OnAudioFocusChangeListener
的onAudioFocusChange
回调中监听音频焦点变化
用来描述该应用使用情况,通过Builder模式进行构建
Usage
(用途):(必须)用于描述音频的使用场景,持续时间,例如AudioAttributes.USAGE_MEDIA
用于媒体播放,AudioAttributes.USAGE_VOICE_COMMUNICATION
用于语音通信等。ContentType
(内容类型):用于描述音频的内容类型,例如AudioAttributes.CONTENT_TYPE_MUSIC
用于音乐,AudioAttributes.CONTENT_TYPE_SPEECH
用于语音等。Flags
(标志):用于指定一些特定的标志,例如AudioAttributes.FLAG_AUDIBILITY_ENFORCED
表示强制性可听性,AudioAttributes.FLAG_HW_AV_SYNC
表示硬件音视频同步等。Legacy Stream Type
(传统流类型):用于与旧版API
兼容,将AudioAttributes
映射到旧版的音频流类型,例如AudioAttributes.STREAM_MUSIC
用来请求获取焦点的详细信息和参数
Builder()
:参数代表请求焦点级别持续时间。setAudioAttributes
:用于描述音频焦点请求的属性setAcceptsDelayedFocusGain
:延迟音频焦点获取是指应用程序在请求音频焦点时,如果焦点当前不可用,是否愿意等待一段时间,直到焦点可用为止;参数默认为true,表示应用程序愿意等待焦点可用。如果将参数设置为false
,则表示应用程序不接受延迟焦点获取,如果焦点当前不可用,则请求会立即返回失败。setWillPauseWhenDucked
:用于设置当应用程序的音频焦点被降低时是否暂停音频播放。当应用程序的音频焦点被其他应用程序请求并且获得时,系统可能会降低应用程序的音量以便让焦点持有者能够播放音频。参数默认值为true
,表示应用程序在焦点被降低时会暂停音频播放。如果将参数设置为false
,则表示应用程序在焦点降低时不会暂停音频播放,而是继续以降低的音量播放。setOnAudioFocusChangeListener
:用于设置监听音频焦点变化的回调接口;当应用程序请求音频焦点并且焦点状态发生变化时,系统会调用注册的AudioFocusChangeListener
接口的回调方法,通知应用程序焦点状态的变化
AudioManager.OnAudioFocusChangeListener() {
@Override
public void onAudioFocusChange(int state) {
switch (focus) {
// 重新获取音频焦点
// 恢复播放
case AudioManager.AUDIOFOCUS_GAIN:
break;
// 永久丢失焦点
// 此时应该停止播放,释放资源
case AudioManager.AUDIOFOCUS_LOSS:
break;
// 短暂丢失焦点
// 但是很快就会重新获得,在此状态应该暂停所有音频播放
case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
break;
// 瞬间丢失焦点
// 暂时失去焦点,但是允许持续播放音频(以很小的声音),不需要完全停止播放
case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:
break;
}
}
};
在Android8.0之前通过以下方法释放焦点,参数为之前申请焦点创建的监听OnAudioFocusChangeListener
。
audioManager.abandonAudioFocus(onAudioFocusChangeListener);
在Android8.0之后则通过如下方法释放,参数为之前申请焦点时创建的AudioFocusRequest
。
audioManager.abandonAudioFocusRequest(audioFocusRequest);
注意主动abandon焦点的时候是不会回调监听的