以下是音频焦点管理的具体实现示例,涵盖不同场景和平台特性:

一、传统Android音频焦点管理(手动请求)

public class MediaPlayerService extends Service {
    private AudioManager audioManager;
    private MediaPlayer mediaPlayer;
    private AudioFocusRequest focusRequest; // Android O+

    @Override
    public void onCreate() {
        audioManager = (AudioManager) getSystemService(AUDIO_SERVICE);
    }

    public void startPlayback() {
        // 1. 请求音频焦点
        if (requestAudioFocus() != AudioManager.AUDIOFOCUS_REQUEST_GRANTED) return;
        
        // 2. 初始化播放器
        mediaPlayer = MediaPlayer.create(this, R.raw.sample);
        mediaPlayer.start();
    }

    private int requestAudioFocus() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            AudioAttributes attrs = new AudioAttributes.Builder()
                .setUsage(AudioAttributes.USAGE_MEDIA)
                .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
                .build();
            
            focusRequest = new AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN)
                .setAudioAttributes(attrs)
                .setOnAudioFocusChangeListener(focusChangeListener)
                .build();
            
            return audioManager.requestAudioFocus(focusRequest);
        } else {
            return audioManager.requestAudioFocus(
                focusChangeListener, 
                AudioManager.STREAM_MUSIC, 
                AudioManager.AUDIOFOCUS_GAIN
            );
        }
    }

    // 3. 焦点变化监听
    private OnAudioFocusChangeListener focusChangeListener = focusChange -> {
        switch (focusChange) {
            case AudioManager.AUDIOFOCUS_LOSS:
                handleStopPlayback(); // 永久失去焦点
                break;
            case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
                mediaPlayer.pause(); // 临时暂停
                break;
            case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:
                mediaPlayer.setVolume(0.3f, 0.3f); // 降低音量
                break;
            case AudioManager.AUDIOFOCUS_GAIN:
                mediaPlayer.setVolume(1.0f, 1.0f); // 恢复音量
                if (!mediaPlayer.isPlaying()) mediaPlayer.start();
                break;
        }
    };

    // 4. 释放资源
    private void releaseResources() {
        if (mediaPlayer != null) {
            mediaPlayer.release();
            mediaPlayer = null;
        }
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            audioManager.abandonAudioFocusRequest(focusRequest);
        } else {
            audioManager.abandonAudioFocus(focusChangeListener);
        }
    }
}

关键处理逻辑

  • 永久失去焦点 (AUDIOFOCUS_LOSS) → 停止播放并释放资源
  • 临时失去焦点 (AUDIOFOCUS_LOSS_TRANSIENT) → 暂停播放
  • 允许闪避 (AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK) → 音量降至30%

二、AAOS并发焦点模式实现

// 适用于车载导航提示音播放
public void playNavigationPrompt() {
    AudioAttributes attrs = new AudioAttributes.Builder()
        .setUsage(AudioAttributes.USAGE_ASSISTANCE_NAVIGATION_GUIDANCE)
        .setContentType(AudioAttributes.CONTENT_TYPE_SPEECH)
        .build();
    
    // 关键配置:MAY_DUCK类型 + 禁用暂停策略
    AudioFocusRequest request = new AudioFocusRequest.Builder(
            AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK)
        .setAudioAttributes(attrs)
        .setWillPauseWhenDucked(false) // 必须禁用暂停策略
        .build();
    
    if (audioManager.requestAudioFocus(request) == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
        // 系统自动降低背景音乐音量至50%
        navigationPlayer.start();
    }
}

// 焦点监听器实现(简化)
private OnAudioFocusChangeListener listener = focusChange -> {
    if (focusChange == AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK) {
        // 无需操作!系统自动处理音量闪避
    }
};

并发模式强制要求

  1. 请求类型必须为 AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK
  2. 必须调用 setWillPauseWhenDucked(false)
  3. 音频属性需明确声明为导航类型 (USAGE_ASSISTANCE_NAVIGATION_GUIDANCE)

三、ExoPlayer自动管理方案

// 自动处理焦点请求/释放(推荐媒体应用使用)
exoPlayer.setAudioAttributes(
    new com.google.android.exoplayer2.audio.AudioAttributes.Builder()
        .setUsage(C.USAGE_MEDIA)
        .setContentType(C.CONTENT_TYPE_MUSIC)
        .build(),
    true // 启用自动焦点管理
);

// 播放时自动触发:
exoPlayer.setMediaItem(MediaItem.fromUri(uri));
exoPlayer.prepare();
exoPlayer.play(); // 内部自动请求焦点

注意:启用自动管理后禁止手动调用音频焦点API,否则会破坏内部状态机。


四、多音区焦点管理(AAOS专用)

<!-- car_audio_configuration.xml 配置示例 -->
<volumeGroups>
    <group>
        <device address="bus0_media_out">
            <context context="music"/> <!-- 媒体焦点组 -->
        </device>
    </group>
    <group>
        <device address="bus1_navigation_out">
            <context context="navigation"/> <!-- 导航焦点组 -->
        </device>
    </group>
</volumeGroups>

实现特性

  • 独立音区独立焦点管理
  • 通过音频上下文 (context) 定义焦点组
  • 支持驾驶员区域优先策略

⚠️ 关键差异总结

场景 焦点请求类型 音量控制方式
传统Android AUDIOFOCUS_GAIN 应用手动调整
AAOS并发模式 AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK 系统自动闪避
ExoPlayer 自动选择 播放器自主管理

引用链接:
1.音频焦点 | Android Open Source Project - Android
2.AAOS 音频焦点请求 - CSDN博客
3.【AAOS】【源码分析】CarAudioService(二)-- 功能介绍 - CSDN博客
4.音量管理 | Android Open Source Project - Android
5.HarmonyOS多音频播放并发政策及音频管理解析 - 博客园
6.android AAOS android aaos 输入 - 51CTO博客
7.关于音频焦点的理解 - CSDN
8.Android Automotive 虚拟化 - 铁阳聊汽车
9.Axios 并发请求实战 - 掌握这 3 种实现技巧 - Apifox
10.Android14音频进阶之AAOS音频焦点(七十一) - CSDN博客
11.简单易懂的方式解决 Axios 并发请求的 3 招 - 51CTO博客
12.网络编程(18)——使用asio协程实现并发服务器demo(官方案例) - 爱吃土豆
13.Automotive audio策略总结 - CSDN博客
14.架构| Android Open Source Project - Android
15.车载音频 - Android
16.Android车载开发启示录|媒体篇-音频焦点 - 稀土掘金
17.并发编程系列-Semaphore - 架构师刘欣
18.HarmonyOS开发中如何实现相机手动对焦? - 思否开发者社区
19.Android车载开发启示录|媒体篇-音频焦点 - 掘金开发者社区
20.Java并发源码AQS - 杨京京
21.鸿蒙5开发宝藏案例分享—应用并发设计 - 腾讯云
22.简体中文 - 华为开发者联盟
23.音频焦点 | Android Open Source Project - Android
24.OpenHarmony音频焦点模式开发 - CSDN博客
25.系统学习AutoSAR ETAS RTA-OS嵌入式操作系统(三)任务Tasks - 艾格北峰汽车电子

Logo

魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。

更多推荐