文章目录
-
- 一、引言
- [二、Android 音频架构分层概览](#二、Android 音频架构分层概览)
- [三、应用层 API:音频播放的入口](#三、应用层 API:音频播放的入口)
-
- [3.1 API 选型指南](#3.1 API 选型指南)
- [3.2 AudioTrack:PCM 播放的核心](#3.2 AudioTrack:PCM 播放的核心)
- [四、Java 框架层:JNI 与 AudioSystem](#四、Java 框架层:JNI 与 AudioSystem)
- [五、Native 核心层](#五、Native 核心层)
-
- [5.1 AudioFlinger:音频中枢](#5.1 AudioFlinger:音频中枢)
- [5.2 PlaybackThread:音频输出线程](#5.2 PlaybackThread:音频输出线程)
- [5.3 AudioPolicyService:策略决策者](#5.3 AudioPolicyService:策略决策者)
- [5.4 数据流核心:共享内存通信](#5.4 数据流核心:共享内存通信)
- [六、HAL 层:硬件抽象](#六、HAL 层:硬件抽象)
-
- [6.1 HAL 的作用](#6.1 HAL 的作用)
- [6.2 从 HIDL 到 AIDL 的演进](#6.2 从 HIDL 到 AIDL 的演进)
- [七、驱动层:从 TinyALSA 到 ALSA](#七、驱动层:从 TinyALSA 到 ALSA)
-
- [7.1 TinyALSA:Android 的轻量级 ALSA](#7.1 TinyALSA:Android 的轻量级 ALSA)
- [7.2 从 TinyALSA 到内核驱动的完整路径](#7.2 从 TinyALSA 到内核驱动的完整路径)
- [7.3 硬件数据传输](#7.3 硬件数据传输)
- 八、完整链路串联:一次音频播放的全流程
- 九、总结
- 参考资料
一、引言
从应用程序写入一段音频数据,到声音从扬声器或耳机发出,这段看似简单的旅程,在 Android 系统中要经过应用进程 → Binder → AudioFlinger → HAL → Kernel → 硬件多个关卡,期间经历共享内存传递、采样率转换、多路混音、格式重排等一系列处理。本文将沿着音频播放这条主线,自上而下地梳理 Android 音频系统的完整链路,从应用层 API 一路深入到内核驱动,帮助读者建立起对整个音频栈的系统性认知。
二、Android 音频架构分层概览
Android 音频系统采用分层设计,从上到下可划分为以下五个层次:
┌───────────────────────────────┐
│ 应用层 API │ ← Java/Kotlin 层:MediaPlayer, SoundPool, AudioTrack
├───────────────────────────────┤
│ Framework 层 (Java) │ ← AudioManager, AudioSystem
├───────────────────────────────┤
│ Native 核心层 │ ← AudioFlinger, AudioPolicyService (libaudioflinger.so)
├───────────────────────────────┤
│ HAL 层 │ ← 厂商实现:audio.primary.xxx.so
├───────────────────────────────┤
│ 驱动层 (Kernel) │ ← ALSA/TinyALSA,控制音频 Codec
└───────────────────────────────┘
无论上层使用哪种 API 进行播放,最终音频数据都会通过 AudioFlinger,并由 Audio HAL 与底层硬件驱动交互,完成音频输出。Audio 系统在 Android 中主要负责音频数据流传输和音频设备管理,不涉及解码功能------解码由 OpenCore 或 StageFright 完成,解码后才调用音频系统接口进行播放。
三、应用层 API:音频播放的入口
3.1 API 选型指南
Android 为开发者提供了多套音频 API,各有不同的定位和适用场景:
| API | 数据源 | 延迟 | 适用场景 |
|---|---|---|---|
| MediaPlayer | 压缩音频文件(MP3/AAC 等) | 较高(100-300ms) | 背景音乐、视频播放 |
| SoundPool | 预加载 PCM 短音效 | 极低 | 按键音、提示音 |
| AudioTrack | 原始 PCM 数据 | 低(10-50ms) | 实时音频、VOIP、自定义播放 |
| AAudio/Oboe | PCM | 超低(<10ms) | 专业音频、高版本设备 |
| OpenSL ES | PCM | 中等 | 旧设备兼容、底层控制 |
3.2 AudioTrack:PCM 播放的核心
MediaPlayer 与 AudioTrack 的本质区别
MediaPlayer 在 framework 层最终也会创建 AudioTrack,把解码后的 PCM 数据流传递给 AudioTrack。MediaPlayer 适合播放现成的音频文件,而 AudioTrack 则面向实时处理场景,需要开发者自行处理 PCM 数据的生成、缓冲与传输。
共享缓冲区机制
AudioTrack 的低延迟核心源于共享缓冲区机制:
- 应用层初始化 AudioTrack 时,与 AudioFlinger 协商创建一块共享内存缓冲区
- 应用层通过
write()方法将 PCM 数据写入共享缓冲区 - AudioFlinger 从共享缓冲区中"拉取"数据,经过混音、格式转换后发送给 HAL
- HAL 驱动音频硬件播放数据
这种"应用层写入 → 系统层拉取"的模型,避免了数据的多次拷贝,显著降低延迟。
两种播放模式
- STREAM 模式:共享缓冲区是环形缓冲区,适合持续播放大体积音频(如音乐、通话)
- STATIC 模式:播放前一次性加载全部 PCM 数据,适合短音效(如提示音),延迟极低
四、Java 框架层:JNI 与 AudioSystem
Java 层的 AudioTrack、AudioManager 等 API 通过 JNI 调用 Native 层的 libmedia.so。JNI 部分代码位于 frameworks/base/core/jni,生成 libandroid_runtime.so。
AudioSystem 是 Java 框架层和 Native 层之间的桥梁,通过 Binder 机制与 AudioFlinger 进行跨进程通信。AudioSystem 负责音量调节、音频设备选择、响铃模式选择等管理功能,而 AudioTrack 负责音频流的数据传输。
五、Native 核心层
5.1 AudioFlinger:音频中枢
AudioFlinger 是 Android 音频系统的核心服务,运行在 audioserver 进程中(Android 8.0 之前运行在 mediaserver 进程中)。它是所有音频流的最终归宿,控制着音频的输出流程。
核心功能
- 多音频流混音:将多个独立的音频流按优先级和音量规则混合成单一音频流。优先级规则为:语音通话流 > 通知流 > 音乐流
- 音频路由:根据 AudioPolicyService 的决策,将音频流导向正确的输出设备(扬声器、耳机、蓝牙等)
- 音频格式转换:统一不同应用输出的采样率、位深和声道数,使其与硬件能力匹配
- 与 Audio HAL 交互:通过 HAL 接口将数据发送至驱动层
主要组件
AudioFlinger 主要包括 Mixer(合并多个音频流)、Track(代表应用程序的音频流)、Output(输出到物理设备)和 Effect(音频效果处理)四大模块。其核心实现位于 frameworks/av/services/audioflinger/AudioFlinger.cpp。
5.2 PlaybackThread:音频输出线程
每个输出设备对应一个 PlaybackThread 实例,核心方法是 threadLoop()。根据场景不同,AudioFlinger 会选择不同类型的播放线程:
| 线程类型 | 特点 | 适用场景 |
|---|---|---|
| MixerThread | 支持混音、重采样、音量调节 | 大多数普通播放场景 |
| DirectOutputThread | 绕过混音器,要求格式与 HAL 严格匹配 | 低延迟场景 |
| OffloadThread | 压缩音频硬件直通 | MP3/AAC 等格式的硬件解码 |
| DuplicatingThread | 一份输入复制到多个输出 | 同时扬声器+蓝牙播放 |
以 MixerThread 为例,其线程循环主要包括三个阶段:prepareTracks_l() 收集有数据的 Track 并计算混音权重 → threadLoop_mix() 从各 Track 共享内存取出数据、重采样并累加到混音缓冲区 → threadLoop_write() 将混音缓冲区写入 HAL。
5.3 AudioPolicyService:策略决策者
AudioPolicyService 是策略的制定者,比如决定什么时候打开音频接口设备、某种 Stream 类型的音频对应什么设备;而 AudioFlinger 则是策略的执行者,负责与音频设备通信、混音等具体操作。
与 AudioFlinger 的职责分离
| 维度 | AudioPolicyService | AudioFlinger |
|---|---|---|
| 角色 | 决策者(指挥官) | 执行者(士兵) |
| 职责 | 路由策略、设备管理、音量策略、音频焦点 | 混音、数据传输、资源管理 |
典型协作场景:当用户插入耳机时,AudioPolicyService 检测设备状态变化,决定新的路由策略,然后通过 AudioFlinger 执行具体的硬件切换操作。
音频策略优先级
AudioPolicyManager 通过优先级系统解决多音频流竞争问题:通话(最高)> 通知音 > 媒体播放 > 辅助功能(最低)。
5.4 数据流核心:共享内存通信
App 与 AudioFlinger 之间通过匿名共享内存进行数据传递,实现零拷贝通信:
- App 创建 AudioTrack 时,通过
IAudioFlinger::createTrack()发起 Binder 调用 - AudioFlinger 分配 Track 对象,并通过 MemoryDealer 分配一块匿名共享内存
- 共享内存头部包含
audio_track_cblk_t控制块,包含 user/server 读写指针、缓冲区大小、等待标志等 - AudioFlinger 将共享内存文件描述符通过 Binder 传回 App 进程
- App 端通过 mmap 将共享内存映射到自己的进程空间
- App 和 AudioFlinger 通过原子操作更新读写指针,实现无锁通信
写入流程 :App 调用 AudioTrack::write() 或 obtainBuffer() + releaseBuffer() 将 PCM 数据拷贝到共享内存的环形缓冲区。写入位置由 user 指针指示,AudioFlinger 侧的读取位置由 server 指针指示。当缓冲区满时,App 进入等待,由 Cblk 中的 futex 机制唤醒。
六、HAL 层:硬件抽象
6.1 HAL 的作用
Android 的音频硬件抽象层(HAL)将 android.media 中较高层级的音频专用框架 API 连接到底层的音频驱动程序和硬件。音频 HAL 定义了音频服务会调用的标准接口,必须实现音频 HAL 才能使音频硬件正常运行。
HAL 层的代码由各 SoC 厂商实现,被编译成动态库(如 audio.primary.xxx.so),供 AudioFlinger 通过 dlopen 动态加载。
6.2 从 HIDL 到 AIDL 的演进
| 版本 | 接口定义语言 | 说明 |
|---|---|---|
| Android 8.x - 13 | HIDL | Project Treble 引入,分离 Framework 和 HAL |
| Android 14 及以上 | AIDL | 统一使用 AIDL,配置规范移至 HAL 内 |
从 Android 14 开始,使用 AIDL 定义音频 HAL 接口。AIDL 音频 HAL 包含 Core HAL(播放和控制主 API)、Effects HAL(音频效果控制)和 Common HAL(共用数据结构)三大模块。
AIDL Core HAL 关键接口:
IModule.aidl:API 入口点IStreamOut.aidl/IStreamIn.aidl:音频流收发ITelephony.aidl:电话功能控件IBluetooth.aidl:BT SCO 和 HFP 控件
七、驱动层:从 TinyALSA 到 ALSA
7.1 TinyALSA:Android 的轻量级 ALSA
Android 使用 TinyALSA 替代标准 ALSA-lib,这是一个为嵌入式移动设备设计的轻量级用户空间库。其设计理念是"去插件化"------移除所有运行时插件机制,音频格式转换等操作由上层处理;精简 API 集合,仅保留 PCM 和 Control 两类核心接口(从 87 个 API 缩减到 23 个)。
TinyALSA 代码位于 external/tinyalsa 目录下,主要提供三个用户空间工具:
tinyplay:播放音频tinycap:录制音频tinymix:控制混音器参数
7.2 从 TinyALSA 到内核驱动的完整路径
以 pcm_open 为例,分析 TinyALSA 如何调用到内核驱动:
第一步:TinyALSA 打开设备节点
c
// external/tinyalsa/pcm_hw.c
static int pcm_hw_open(unsigned int card, unsigned int device,
unsigned int flags, void **data, void *node) {
snprintf(fn, sizeof(fn), "/dev/snd/pcmC%uD%u%c",
card, device, flags & PCM_IN ? 'c' : 'p');
fd = open(fn, O_RDWR | O_NONBLOCK);
}
在 Linux 设备中,音频节点的主设备号都是 116。例如 /dev/snd/pcmC0D0p 的主设备号为 116,次设备号为 2。
第二步:内核中的设备注册
在 Linux Kernel 中,主设备号为 116 的设备通过 alsa_sound_init() 注册:
c
// sound/core/sound.c
static int __init alsa_sound_init(void) {
if (register_chrdev(major, "alsa", &snd_fops)) {
// 主设备号 major = CONFIG_SND_MAJOR = 116
}
}
注册的 snd_fops 文件操作结构体是所有 ALSA 音频设备的统一入口。
第三步:ASoC 驱动框架
Android 内核中的音频驱动基于 ASoC(ALSA System on Chip)框架,分为三大组件:
- Platform:对应 SoC 的 DMA 引擎和 I2S 控制器
- Codec:音频编解码芯片驱动
- Machine:连接 Platform 和 Codec,定义硬件板级的音频通路
7.3 硬件数据传输
内核声卡驱动使用 DMA 通道将样本从内存传输到声卡的硬件缓冲区。对于播放场景,应用程序通过 DMA 将自己的缓冲区数据传送到声卡的硬件环形缓冲区中。
具体数据路径:
- HAL 模块的
out_write()将用户空间的音频数据拷贝到内核 ALSA DMA 缓冲区 - DMA 控制器将 DMA 缓冲区中的数据拷贝到 SoC 的 I2S Tx FIFO
- 通过 I2S 等数字音频接口将数据送往音频 Codec
- Codec 完成数模转换(DAC)后,驱动扬声器或耳机发声
Codec 与 SoC 之间通过 I2C 总线(用于寄存器配置)和 DAI(数字音频接口,用于音频数据传输)进行通信。
八、完整链路串联:一次音频播放的全流程
将以上所有层次串联起来,一次音频播放的完整链路如下:
- App 调用 :应用调用
AudioTrack.write(PCM_data) - 写入共享内存:PCM 数据被拷贝到 App 与 AudioFlinger 之间的匿名共享内存环形缓冲区
- Binder 通知:App 通过 Binder 通知 AudioFlinger 有新数据可用
- AudioFlinger 拉取:AudioFlinger 的 PlaybackThread 从共享内存读取数据
- 混音处理:MixerThread 将多个 Track 的数据混音,并进行重采样、格式转换
- AudioPolicyService 决策:根据设备状态(耳机/扬声器/蓝牙)决定输出路由
- HAL 调用 :AudioFlinger 调用 HAL 层的
out_write()接口 - TinyALSA :HAL 通过 TinyALSA 调用内核驱动接口(
ioctl或直接操作/dev/snd/*) - ALSA 驱动:内核 ALSA 驱动接收数据,放入 DMA 缓冲区
- DMA 传输:DMA 控制器将数据通过 I2S 总线传输给 Codec
- 硬件播放:Codec 完成 DAC 转换,驱动扬声器或耳机发声
九、总结
Android 音频系统是一个层次清晰、职责分明的复杂系统。从上层的应用 API 到底层的内核驱动,每一层都承担着特定的职责:
- 应用层:提供多样化的 API(MediaPlayer、AudioTrack、AAudio 等),满足不同场景的音频播放需求
- 框架层:通过 JNI 连接 Java 和 Native,AudioSystem 提供系统级音频管理
- Native 核心层:AudioFlinger 负责音频数据的混音、格式转换和路由执行;AudioPolicyService 负责策略决策和设备管理;两者通过共享内存实现高效的零拷贝数据传输
- HAL 层:定义标准硬件接口,从 HIDL 演进到 AIDL,隔离厂商实现与系统框架
- 驱动层:TinyALSA 提供轻量级用户空间接口,内核 ALSA/ASoC 框架完成真正的硬件控制,DMA + I2S + Codec 实现最终的声音输出
理解这一整套链路,不仅有助于写出高性能、低延迟的音频程序,也是进行音频系统定制和调试的必备基础。希望本文能为读者在 Android 音频开发之路上提供一份清晰的路线图。
参考资料
- Android 音频架构全解析:从 AudioTrack 到 AudioFlinger
- 一文了解 Android 中的 AudioFlinger
- Android Audio 架构分析
- AIDL 和 HIDL 的 AudioHal 对比
- TinyALSA 全解析:从 TinyALSA 到底层音频驱动的全流程分析
- Android 音频数据流笔记
- Android 音频架构核心:深入解析 AudioPolicyService
- Android 音频设备切换背后的秘密
- AOSP 源码(Android 14/15)