9、多媒体系统:音频焦点管理(Audio Focus),CarAudioService 与音频路由策略

音频系统在车载环境里,说实话,比手机复杂得多。

手机嘛,基本上就是一个人用。但车里呢?司机要导航,副驾在听歌,后排小孩可能在看动画片。这些声音怎么共存?谁该出声,谁该闭嘴?这就是我们今天要聊的核心——音频焦点管理和路由策略。

我个人习惯把车载音频系统拆成三个层面来看:应用层(谁在发声)、服务层(谁有权限)、硬件层(声音从哪出来)。今天重点讲中间那层。

9.1 音频焦点:谁说了算?

先问一个问题:如果导航说“前方300米右转”,同时音乐正在高潮部分,你希望哪个声音更清晰?

答案很明显。导航必须清晰,音乐可以压低。这就是音频焦点要解决的问题。

核心概念:音频焦点(Audio Focus)是 Android 系统用来协调多个应用同时播放音频的机制。只有获得焦点的应用,才能正常发声。其他应用要么暂停,要么降低音量。

在 Android Automotive OS 里,焦点的管理比手机更严格。手机里你还可以让两个应用同时出声,车里不行——尤其是涉及安全提示的场景。

9.1.1 焦点请求的类型

应用在播放音频前,必须先请求焦点。请求时得告诉系统:你要干嘛?有多重要?

请求类型 说明 典型场景
AUDIOFOCUS_GAIN 长期占用焦点,其他应用必须暂停 音乐播放、视频播放
AUDIOFOCUS_GAIN_TRANSIENT 短暂占用,其他应用暂停一下 导航提示、语音指令
AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK 短暂占用,但允许其他应用降低音量继续播 消息通知、低优先级提示音
AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE 独占焦点,连降低音量都不行 紧急广播、电话通话

我在项目中遇到过一个问题:某个音乐应用在导航播报时,没有正确处理焦点丢失。结果导航说完了,音乐也不恢复了。用户以为系统坏了。后来查代码,发现是焦点监听回调里忘了调用 abandonAudioFocus()

避坑指南:我曾经调试过一个案例,第三方音乐应用在请求 GAIN_TRANSIENT_MAY_DUCK 时,把 duck 音量设成了 0。这相当于让其他应用静音了。记住:duck 的意思是“降低”,不是“静音”。建议 duck 系数控制在 0.3~0.5 之间。

9.1.2 焦点变化的回调处理

应用需要注册一个 OnAudioFocusChangeListener,当焦点状态变化时,系统会回调这个方法:

audioManager.requestAudioFocus(
    new AudioManager.OnAudioFocusChangeListener() {
        @Override
        public void onAudioFocusChange(int focusChange) {
            switch (focusChange) {
                case AudioManager.AUDIOFOCUS_GAIN:
                    // 获得焦点,恢复播放
                    player.start();
                    break;
                case AudioManager.AUDIOFOCUS_LOSS:
                    // 永久失去焦点,停止播放
                    player.stop();
                    break;
                case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
                    // 暂时失去焦点,暂停播放
                    player.pause();
                    break;
                case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:
                    // 降低音量继续播
                    player.setVolume(0.3f);
                    break;
            }
        }
    },
    AudioManager.STREAM_MUSIC,
    AudioManager.AUDIOFOCUS_GAIN
);

嗯,这里要注意:很多开发者只处理了 GAINLOSS,忽略了 CAN_DUCK。结果导航播报时,音乐直接停了,体验很差。

9.2 CarAudioService:车载音频的总管家

CarAudioService 是 Android Automotive 里专门为车载场景设计的音频服务。它比普通的 AudioService 多了几个关键能力:

  • 音频分区管理:不同座位可以有不同的音频策略
  • 音量分组:导航、电话、媒体可以独立调节音量
  • 路由策略:声音从哪个扬声器出来,由 CarAudioService 决定

说白了,CarAudioService 就是车载音频的大脑。它知道现在谁在开车,谁在听歌,哪个声音更重要。

9.2.1 音频分区的概念

在车里,不同座位的人可能想听不同的内容。司机需要导航,副驾想听歌,后排可能在看电影。CarAudioService 通过 音频分区(Audio Zone) 来实现这个需求。

每个分区可以有自己的:

  • 音量控制
  • 焦点管理
  • 音频路由

举个例子:司机区的导航提示,不会影响到副驾正在播放的音乐。这在传统 Android 里是做不到的。

9.2.2 音量分组策略

CarAudioService 把音频流分成了几个组,每个组可以独立调节音量:

音量组 包含的音频类型 典型场景
媒体 音乐、视频、游戏 听歌、看电影
导航 导航语音、路况提示 导航播报
通话 电话、VoIP 蓝牙电话
系统 提示音、警告音 安全带提醒、转向灯
紧急 紧急广播、安全警告 碰撞预警、故障报警

我建议在配置音量组时,把导航和紧急组的最大音量设得高一些。因为安全相关的提示,必须能在任何环境下被听到。

9.3 音频路由策略:声音从哪出来?

音频路由,说白了就是决定声音从哪个扬声器播放。手机很简单,要么听筒要么扬声器。车里呢?可能有十几个扬声器。

CarAudioService 的路由策略基于几个因素:

  • 音频类型:导航走前门扬声器,电话走头枕扬声器
  • 用户位置:司机的声音从左边出,副驾从右边出
  • 当前状态:倒车时,所有非安全音频都要降低或静音

核心逻辑:路由策略的优先级是:安全 > 通信 > 导航 > 媒体。紧急广播可以打断一切,电话可以压低音乐,导航可以 duck 媒体。

9.3.1 动态路由示例

假设这样一个场景:

  1. 司机在听音乐(媒体流,前门扬声器)
  2. 副驾在打电话(通话流,头枕扬声器)
  3. 导航突然播报(导航流,前门扬声器)

CarAudioService 会怎么处理?

  • 导航获得焦点,音乐被 duck(音量降到 30%)
  • 通话不受影响,因为走的是不同的扬声器
  • 导航播报结束后,音乐恢复音量

你看,这就是分区和路由配合的效果。不同音频流可以互不干扰。

9.4 知识体系总览

下面这张图,是我梳理的音频焦点与路由的整体架构。建议你多看几遍,把每个模块的职责搞清楚。

车载音频系统架构总览 应用层 音乐App | 导航App | 电话App | 语音助手 | 系统提示 音频焦点管理 (Audio Focus) 焦点请求 → 焦点分配 → 焦点回调 → 音量/状态调整 CarAudioService 音频分区管理 | 音量分组 | 路由策略决策 硬件抽象层 (Audio HAL / 音频路由) 扬声器阵列 | 功放控制 | 音频通道切换

9.5 实战中的几个坑

最后,分享几个我在实际项目中踩过的坑:

坑一:焦点泄漏

应用在退出时没有释放焦点。结果其他应用永远拿不到焦点。我曾经排查过一个 case,导航播报一直没声音,最后发现是一个音乐 App 在后台死锁了焦点。

坑二:路由冲突

两个音频流被路由到了同一个扬声器通道。结果声音混在一起,谁也听不清。解决方案是在 CarAudioService 里配置好每个音频类型的默认路由,并加上冲突检测。

建议:在开发阶段,打开 CarAudioService 的日志开关。把焦点变化和路由决策都打出来。你会发现很多隐藏的问题。

好了,这一章的内容就到这里。音频焦点和路由策略,是车载多媒体系统的基石。搞懂了这两个东西,你就能理解为什么车里不同声音可以互不干扰,也能知道怎么去设计和调试一个复杂的车载音频系统。


公众号:蓝海资料掘金营,微信deep3321