简述Android中的音频焦点处理

3 阅读4分钟

在 Android 中,音频焦点(Audio Focus)是协调多个应用音频输出的核心机制,确保同一时间只有一个应用能控制音频播放。以下是申请和管理音频焦点的详细流程:

一、音频焦点的核心概念

  1. 作用:避免多个应用同时播放音频(如音乐与导航冲突),通过「焦点抢占」机制实现有序播放。

  2. 关键组件

    • 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 版本的适配

  1. API 26 以下(Android 8.0 之前)

    java

    // 使用旧 API 请求焦点
    AudioManager.OnAudioFocusChangeListener listener = this;
    int result = audioManager.requestAudioFocus(
        listener,
        AudioManager.STREAM_MUSIC, // 音频流类型
        AudioManager.AUDIOFOCUS_GAIN // 焦点类型
    );
    
  2. Android 12(API 31)及以上

    • 系统强制管理:当其他应用请求焦点时,当前应用音频会自动淡出(Fade Out),开发者无需手动处理。
    • 适配建议:确保音频属性正确设置(如 USAGE_MEDIA),系统会根据策略自动处理焦点变化。

四、焦点类型与应用场景

  1. AUDIOFOCUS_GAIN(长期焦点):

    • 场景:音乐播放器、视频应用。
    • 行为:其他应用需停止播放(如来电时音乐暂停)。
  2. AUDIOFOCUS_GAIN_TRANSIENT(短暂焦点):

    • 场景:导航提示、语音消息。
    • 行为:其他应用暂停播放(如导航时音乐暂停)。
  3. AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK(短暂焦点,允许降低音量):

    • 场景:通知提示、实时翻译。
    • 行为:其他应用降低音量继续播放(如通知音响起时音乐音量降低)。
  4. AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE(短暂独占焦点):

    • 场景:语音通话、语音识别。
    • 行为:其他应用完全停止播放(如通话时音乐暂停)。

五、常见问题与最佳实践

  1. 焦点申请失败处理

    • 若请求失败(result != AUDIOFOCUS_REQUEST_GRANTED),应暂停播放并提示用户(如 “无法播放音频,其他应用正在使用音频设备”)。
  2. 多音频源协调

    • 同一应用内多个音频流(如音乐 + 语音提示)需共享焦点:

      java

      // 复用同一 AudioFocusRequest
      focusRequest = new AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN)
          .setAudioAttributes(audioAttributes)
          .build();
      
  3. ExoPlayer 自动管理焦点

    java

    // 设置 ExoPlayer 自动处理音频焦点
    AudioAttributes audioAttributes = new AudioAttributes.Builder()
        .setUsage(AudioAttributes.USAGE_MEDIA)
        .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
        .build();
    simpleExoPlayer.setAudioAttributes(audioAttributes, true); // 启用自动焦点管理
    
  4. 音频路由切换(如蓝牙断开)

    • 监听音频路由变化,重新申请焦点:

      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;
        }
    }
}

七、总结

  1. 核心流程:申请焦点 → 处理回调 → 释放焦点。

  2. 版本适配:优先使用 API 26+ 的 AudioFocusRequest,兼容旧版本。

  3. 场景选择:根据音频类型选择合适的焦点类型(如音乐用 AUDIOFOCUS_GAIN,导航用 TRANSIENT_MAY_DUCK)。

  4. 系统强制管理(Android 12+) :无需手动处理焦点丢失,系统自动协调音频淡出。

通过合理管理音频焦点,可确保应用在复杂场景下的音频播放体验,避免与其他应用冲突,提升用户满意度。