在 Android 中,音频焦点(Audio Focus)是协调多个应用音频输出的核心机制,确保同一时间只有一个应用能控制音频播放。以下是申请和管理音频焦点的详细流程:
一、音频焦点的核心概念
-
作用:避免多个应用同时播放音频(如音乐与导航冲突),通过「焦点抢占」机制实现有序播放。
-
关键组件:
- AudioManager:系统服务,负责管理音频焦点。
- AudioFocusRequest(API 26+):封装焦点请求参数,包括类型、优先级、回调等。
- OnAudioFocusChangeListener:监听焦点变化(如失去焦点时暂停播放)。
二、申请音频焦点的步骤
1. 创建 AudioFocusRequest(API 26+ 推荐)
java
// 构建音频属性(描述音频用途)
AudioAttributes audioAttributes = new AudioAttributes.Builder()
.setUsage(AudioAttributes.USAGE_MEDIA) // 媒体播放
.setContentType(AudioAttributes.CONTENT_TYPE_MUSIC) // 音乐内容
.build();
// 构建焦点请求
AudioFocusRequest focusRequest = new AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN)
.setAudioAttributes(audioAttributes) // 关联音频属性
.setOnAudioFocusChangeListener(this) // 设置焦点变化回调
.setWillPauseWhenDucked(true) // 被其他应用降低音量时暂停播放
.build();
2. 请求音频焦点
java
AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
int result = audioManager.requestAudioFocus(focusRequest);
if (result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
// 焦点申请成功,开始播放音频
mediaPlayer.start();
} else {
// 申请失败,处理错误(如提示用户或静默处理)
}
3. 处理焦点变化回调
java
@Override
public void onAudioFocusChange(int focusChange) {
switch (focusChange) {
case AudioManager.AUDIOFOCUS_GAIN:
// 重新获得焦点,恢复播放
mediaPlayer.setVolume(1.0f, 1.0f);
mediaPlayer.start();
break;
case AudioManager.AUDIOFOCUS_LOSS:
// 永久失去焦点,停止播放并释放资源
mediaPlayer.stop();
mediaPlayer.release();
break;
case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
// 短暂失去焦点,暂停播放
mediaPlayer.pause();
break;
case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:
// 允许降低音量继续播放
mediaPlayer.setVolume(0.3f, 0.3f);
break;
}
}
4. 释放音频焦点
java
// 播放结束或应用暂停时释放焦点
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
audioManager.abandonAudioFocusRequest(focusRequest);
} else {
audioManager.abandonAudioFocus(this); // 旧 API 方式
}
三、不同 Android 版本的适配
-
API 26 以下(Android 8.0 之前) :
java
// 使用旧 API 请求焦点 AudioManager.OnAudioFocusChangeListener listener = this; int result = audioManager.requestAudioFocus( listener, AudioManager.STREAM_MUSIC, // 音频流类型 AudioManager.AUDIOFOCUS_GAIN // 焦点类型 );
-
Android 12(API 31)及以上:
- 系统强制管理:当其他应用请求焦点时,当前应用音频会自动淡出(Fade Out),开发者无需手动处理。
- 适配建议:确保音频属性正确设置(如
USAGE_MEDIA
),系统会根据策略自动处理焦点变化。
四、焦点类型与应用场景
-
AUDIOFOCUS_GAIN(长期焦点):
- 场景:音乐播放器、视频应用。
- 行为:其他应用需停止播放(如来电时音乐暂停)。
-
AUDIOFOCUS_GAIN_TRANSIENT(短暂焦点):
- 场景:导航提示、语音消息。
- 行为:其他应用暂停播放(如导航时音乐暂停)。
-
AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK(短暂焦点,允许降低音量):
- 场景:通知提示、实时翻译。
- 行为:其他应用降低音量继续播放(如通知音响起时音乐音量降低)。
-
AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE(短暂独占焦点):
- 场景:语音通话、语音识别。
- 行为:其他应用完全停止播放(如通话时音乐暂停)。
五、常见问题与最佳实践
-
焦点申请失败处理:
- 若请求失败(
result != AUDIOFOCUS_REQUEST_GRANTED
),应暂停播放并提示用户(如 “无法播放音频,其他应用正在使用音频设备”)。
- 若请求失败(
-
多音频源协调:
-
同一应用内多个音频流(如音乐 + 语音提示)需共享焦点:
java
// 复用同一 AudioFocusRequest focusRequest = new AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN) .setAudioAttributes(audioAttributes) .build();
-
-
ExoPlayer 自动管理焦点:
java
// 设置 ExoPlayer 自动处理音频焦点 AudioAttributes audioAttributes = new AudioAttributes.Builder() .setUsage(AudioAttributes.USAGE_MEDIA) .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC) .build(); simpleExoPlayer.setAudioAttributes(audioAttributes, true); // 启用自动焦点管理
-
音频路由切换(如蓝牙断开) :
-
监听音频路由变化,重新申请焦点:
java
// 注册音频路由变化监听 audioManager.registerAudioRoutingCallback(new AudioRoutingCallback() { @Override public void onAudioRoutesChanged(AudioRouting audioRouting) { // 重新请求音频焦点 audioManager.requestAudioFocus(focusRequest); } });
-
六、代码示例:完整音频焦点管理
java
public class AudioPlayer implements AudioManager.OnAudioFocusChangeListener {
private MediaPlayer mediaPlayer;
private AudioManager audioManager;
private AudioFocusRequest focusRequest;
public void init(Context context) {
audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
mediaPlayer = MediaPlayer.create(context, R.raw.music);
// 构建音频属性和焦点请求
AudioAttributes audioAttributes = new AudioAttributes.Builder()
.setUsage(AudioAttributes.USAGE_MEDIA)
.setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
.build();
focusRequest = new AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN)
.setAudioAttributes(audioAttributes)
.setOnAudioFocusChangeListener(this)
.setWillPauseWhenDucked(true)
.build();
}
public void startPlayback() {
int result = audioManager.requestAudioFocus(focusRequest);
if (result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
mediaPlayer.start();
}
}
public void stopPlayback() {
mediaPlayer.stop();
audioManager.abandonAudioFocusRequest(focusRequest);
}
@Override
public void onAudioFocusChange(int focusChange) {
switch (focusChange) {
case AudioManager.AUDIOFOCUS_GAIN:
mediaPlayer.setVolume(1.0f, 1.0f);
mediaPlayer.start();
break;
case AudioManager.AUDIOFOCUS_LOSS:
mediaPlayer.stop();
mediaPlayer.release();
break;
case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
mediaPlayer.pause();
break;
case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:
mediaPlayer.setVolume(0.3f, 0.3f);
break;
}
}
}
七、总结
-
核心流程:申请焦点 → 处理回调 → 释放焦点。
-
版本适配:优先使用 API 26+ 的
AudioFocusRequest
,兼容旧版本。 -
场景选择:根据音频类型选择合适的焦点类型(如音乐用
AUDIOFOCUS_GAIN
,导航用TRANSIENT_MAY_DUCK
)。 -
系统强制管理(Android 12+) :无需手动处理焦点丢失,系统自动协调音频淡出。
通过合理管理音频焦点,可确保应用在复杂场景下的音频播放体验,避免与其他应用冲突,提升用户满意度。