一、音频系统全景:Android音频架构概览

各位好,我是老张。做Android音频系统开发十几年了,今天咱们聊聊整个音频架构的全貌。

很多人一上来就扎进AudioFlinger的代码里,结果越看越懵。我当年也是这样,后来才明白——你得先看清地图,再上路。

Android音频架构,说白了就是三层:应用层、框架层、硬件层。应用层你天天见,就是那些播放音乐、打电话的App。框架层是核心,AudioFlinger和AudioPolicyService是两大主角。硬件层就是HAL,跟芯片厂商打交道的地方。

核心要点:音频数据从App产生,经过框架层处理,最终通过HAL送到硬件。这个路径上,每一步都有讲究。

1.1 整体架构分层

我习惯把Android音频架构画成一张图,这样看最清楚:

应用层 (Application Layer) MediaPlayer | AudioTrack | AudioRecord | 电话App 框架层 (Framework Layer) AudioFlinger 混音、播放、录制、效果处理 管理音频输出/输入流 AudioPolicyService 音频策略决策、路由选择 设备管理、音量策略 硬件抽象层 (HAL Layer) Audio HAL | 厂商实现 | 驱动接口 Binder IPC HAL接口调用

这张图我画了很多遍,每次给新人讲都用它。你看,数据流是自上而下的,但控制流是双向的。

二、AudioFlinger与AudioPolicyService角色分工

这两个服务经常被搞混。我面试的时候问过很多人,十个有八个说不清楚。其实它们的分工很明确:

对比维度 AudioFlinger AudioPolicyService
核心职责 音频数据的搬运工 音频策略的决策者
具体工作 混音、播放、录制、音效 路由选择、设备切换、音量管理
谁说了算 听PolicyService的指挥 根据策略做决策
类比 音响工程师(负责调音) 导演(负责调度)

2.1 AudioFlinger:音频数据的"大管家"

AudioFlinger是Android音频系统的核心服务。它运行在一个独立的进程中,负责所有音频数据的混音和路由。

我举个例子你就明白了:假设你同时开了网易云音乐和微信语音,这两个App各自创建了AudioTrack。AudioFlinger会把两路音频数据混在一起,然后统一送到扬声器。没有它,你只能听到一路声音。

个人经验:我在调试一个车载项目时,发现导航语音和音乐播放总是冲突。后来定位到是AudioFlinger的混音策略配置有问题。说白了,就是两个Track的优先级没设对。

AudioFlinger的主要工作包括:

  • 管理音频输出/输入流:每个音频设备对应一个混音线程(MixerThread)
  • 混音处理:把多个音频源混合成一路PCM数据
  • 音效处理:调用AudioEffect链处理音频数据
  • 与HAL交互:通过HAL接口读写硬件设备

2.2 AudioPolicyService:音频策略的"大脑"

AudioPolicyService不碰音频数据,它只做决策。比如:

  • 插上耳机后,声音从扬声器切换到耳机——这是PolicyService决定的
  • 来电时音乐暂停——这也是PolicyService的策略
  • 蓝牙A2DP连接后,音乐路由到蓝牙耳机——还是PolicyService

为什么会这样设计?你想想看,如果把策略逻辑写在AudioFlinger里,每次改需求都得动核心代码。分开之后,厂商只需要定制PolicyService的配置文件就行。

避坑指南:我曾经遇到一个bug,插拔耳机后声音切换延迟很大。查了两天才发现是PolicyService里设备连接状态更新的回调没处理好。嗯,这种问题在定制ROM时特别常见。

三、音频HAL层简介

HAL层是Android音频系统的"最后一公里"。它定义了统一的接口,让上层代码不用关心底层用的是哪家芯片。

我刚开始接触HAL时觉得这层很简单,不就是几个open/write/close函数吗?后来才发现,坑都在细节里。

3.1 HAL接口结构

Android音频HAL的核心接口定义在hardware/libhardware/include/hardware/audio.h中。主要结构体有:

// 音频设备描述符
struct audio_hw_device {
    struct hw_device_t common;
    
    // 创建/销毁音频流
    int (*open_output_stream)(...);
    int (*open_input_stream)(...);
    void (*close_output_stream)(...);
    void (*close_input_stream)(...);
    
    // 设备控制
    int (*set_master_volume)(...);
    int (*set_mode)(...);
};

// 音频输出流
struct audio_stream_out {
    struct audio_stream common;
    
    // 写入音频数据
    ssize_t (*write)(struct audio_stream_out *stream, 
                     const void *buffer, size_t bytes);
    
    // 获取延迟
    uint32_t (*get_latency)(...);
    
    // 设置音量
    int (*set_volume)(...);
};

你看,接口设计得很简洁。但实际实现时,每个厂商都有自己的"小九九"。比如高通的HAL实现就比MTK的复杂得多,因为要处理更多的音频路径。

3.2 HAL层的实际工作

HAL层到底在做什么?我总结了几点:

  • 设备抽象:把扬声器、耳机、麦克风等物理设备抽象成统一的接口
  • 数据搬运:从AudioFlinger拿到PCM数据,写入硬件驱动
  • 参数配置:设置采样率、位深、声道数等硬件参数
  • 延迟控制:管理音频缓冲区,控制播放延迟

关键认知:HAL层是Android音频系统里最"脏"的活。它要跟各种硬件打交道,还要处理驱动bug、兼容性问题。我见过最离谱的,是某款芯片的HAL里居然有个while循环在等硬件就绪,直接把UI线程卡死了。

3.3 HAL层的演进

Android音频HAL经历了几个版本:

  • Legacy HAL:Android 4.x之前,接口简单但扩展性差
  • Audio HAL 2.0:Android 8.0引入,使用HIDL接口
  • Audio HAL 3.0:Android 10开始,进一步模块化
  • Audio HAL 4.0:Android 12+,支持更多场景

我个人建议,新项目直接上HAL 4.0。虽然迁移成本高,但后面维护起来省心很多。我之前有个项目用的还是Legacy HAL,每次加新功能都得改一堆代码,苦不堪言。

小技巧:调试HAL层问题时,可以打开AudioFlinger的日志:adb shell setprop log.tag.AudioFlinger V。这样能看到每次HAL调用的详细信息,定位问题快很多。

好了,这一章的内容就到这里。音频架构的全景图、两大服务的分工、HAL层的定位,这些是后续所有章节的基础。你把这些搞清楚了,后面学路由策略、设备管理、延迟优化都会轻松很多。


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