音频焦点 Android Audio Focus
Android 音频焦点详解
音频焦点(Audio Focus)是 Android 系统用于协调多个应用同时访问音频输出的机制。当多个应用需要播放音频时,音频焦点确保用户听到的内容不会混乱(如多个音乐应用同时播放)。以下从核心概念、使用场景和代码实现三个方面展开说明。
一、音频焦点的核心概念
-
音频焦点的类型
- 永久性焦点:长时间占用焦点(如音乐播放器)。
- 短暂性焦点:临时占用焦点(如导航提示音)。
- Ducking:短暂降低其他应用音量(如通知音)。
-
焦点请求模式
AUDIOFOCUS_GAIN
:请求长期焦点,其他应用需停止播放。AUDIOFOCUS_GAIN_TRANSIENT
:短暂占用焦点,其他应用需暂停。AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK
:短暂占用焦点,其他应用降低音量(Ducking)。AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE
:短暂独占焦点(如语音录制)。
-
焦点丢失处理
当其他应用请求焦点时,当前应用需根据情况暂停播放、停止播放或降低音量。
二、代码实现与分析
1. 请求音频焦点
使用 AudioManager
请求焦点,并监听焦点变化。
// 初始化 AudioManager
AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
// 创建焦点变化监听器
AudioManager.OnAudioFocusChangeListener afChangeListener = new AudioManager.OnAudioFocusChangeListener() {
@Override
public void onAudioFocusChange(int focusChange) {
switch (focusChange) {
case AudioManager.AUDIOFOCUS_GAIN:
// 重新获得焦点,恢复播放
mediaPlayer.start();
mediaPlayer.setVolume(1.0f, 1.0f);
break;
case AudioManager.AUDIOFOCUS_LOSS:
// 永久丢失焦点,停止播放并释放资源
mediaPlayer.stop();
audioManager.abandonAudioFocus(afChangeListener);
break;
case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
// 暂时丢失焦点,暂停播放
mediaPlayer.pause();
break;
case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:
// 短暂降低音量
mediaPlayer.setVolume(0.2f, 0.2f);
break;
}
}
};
// 请求音频焦点(以 AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK 为例)
int result = audioManager.requestAudioFocus(
afChangeListener,
AudioManager.STREAM_MUSIC,
AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK);
if (result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
// 焦点获取成功,开始播放
mediaPlayer.start();
} else {
// 焦点获取失败,处理逻辑
}
2. 释放音频焦点
在播放结束或应用暂停时释放焦点:
audioManager.abandonAudioFocus(afChangeListener);
3. Android 8.0+ 的 AudioFocusRequest(API 26+)
对于 Android 8.0 及以上设备,使用 AudioFocusRequest
更灵活:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
AudioFocusRequest focusRequest = new AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN)
.setAudioAttributes(new AudioAttributes.Builder()
.setUsage(AudioAttributes.USAGE_MEDIA)
.setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
.build())
.setAcceptsDelayedFocusGain(true) // 允许延迟获取焦点
.setOnAudioFocusChangeListener(afChangeListener)
.build();
int result = audioManager.requestAudioFocus(focusRequest);
}
三、使用函数
-
生命周期管理
- 在
onPause()
或onStop()
中释放焦点。 - 在
onResume()
中重新请求焦点(视场景而定)。
- 在
-
Ducking 实现
在AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK
回调中降低音量,而非暂停播放。 -
处理延迟焦点
Android 8.0+ 支持延迟获取焦点,需在AudioFocusRequest
中配置setAcceptsDelayedFocusGain(true)
。
四、实际使用
通话打断音乐
一、通话打断音乐的流程
-
电话应用的优先级
通话属于高优先级音频场景,系统会强制其他应用让出音频焦点。当来电时,电话应用会请求AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE
类型的焦点(短暂独占),以确保通话音频的独占性。 -
音乐播放器的响应
音乐播放器在失去焦点时,会通过注册的OnAudioFocusChangeListener
收到AUDIOFOCUS_LOSS
或AUDIOFOCUS_LOSS_TRANSIENT
回调,此时需暂停播放。 -
通话结束后的恢复
当通话结束时,电话应用释放焦点,音乐播放器可能重新获得焦点(需主动重新请求),恢复播放。
二、代码示例与分析
1. 音乐播放器的焦点处理
// 初始化 AudioManager 和焦点监听器
AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
MediaPlayer mediaPlayer = new MediaPlayer();
AudioManager.OnAudioFocusChangeListener afChangeListener =
new AudioManager.OnAudioFocusChangeListener() {
@Override
public void onAudioFocusChange(int focusChange) {
switch (focusChange) {
case AudioManager.AUDIOFOCUS_LOSS:
// 永久丢失焦点(如通话开始)
mediaPlayer.pause();
audioManager.abandonAudioFocus(this); // 主动释放焦点
break;
case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
// 暂时丢失焦点(如短暂通话提示)
mediaPlayer.pause();
break;
case AudioManager.AUDIOFOCUS_GAIN:
// 重新获得焦点(如通话结束)
if (!mediaPlayer.isPlaying()) {
mediaPlayer.start();
}
break;
}
}
};
// 播放音乐前请求焦点
int result = audioManager.requestAudioFocus(
afChangeListener,
AudioManager.STREAM_MUSIC,
AudioManager.AUDIOFOCUS_GAIN
);
if (result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
mediaPlayer.start();
}
2. 电话应用的行为(系统级实现)
电话应用的音频焦点请求由系统自动处理,开发者无需手动实现。其核心逻辑类似:
// 系统电话应用的简化逻辑
audioManager.requestAudioFocus(
phoneFocusListener,
AudioManager.STREAM_VOICE_CALL,
AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE
);
三、关键注意事项
-
无需手动处理通话打断
音乐播放器只需正确实现OnAudioFocusChangeListener
,系统会自动触发暂停逻辑。开发者无需监听通话状态。 -
恢复播放的策略
- 如果通话短暂(如未接来电),焦点可能自动恢复(
AUDIOFOCUS_GAIN
),音乐自动播放。 - 若通话时间较长(如持续通话),建议在
onResume()
中重新请求焦点。
- 如果通话短暂(如未接来电),焦点可能自动恢复(
-
与其他高优先级场景的兼容
除通话外,导航提示、警报声等也会通过音频焦点机制中断音乐,处理逻辑一致。 -
Android 8.0+ 的适配
在 Android 8.0 及以上版本,建议使用AudioFocusRequest
对象(需检查版本):if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { AudioFocusRequest focusRequest = new AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN) .setAudioAttributes(new AudioAttributes.Builder() .setUsage(AudioAttributes.USAGE_MEDIA) .build()) .setOnAudioFocusChangeListener(afChangeListener) .build(); audioManager.requestAudioFocus(focusRequest); }