7、AAudio入门:AAudio架构概览、AAudioStream的创建与配置、AAudio与AudioTrack/AudioRecord的对比
好,咱们进入第七章。说实话,AAudio 这个知识点,是我在 Android 音频开发里绕不开的一个坎。我记得第一次接触它是在一个直播项目里,那时候 AudioTrack 的延时让我头疼得要命。后来 Google 推出了 AAudio,我才算真正找到了低延时音频的钥匙。
这一章,我会带你从架构、创建、到对比,把 AAudio 的底裤扒干净。你想想看,搞懂了它,你就能在实时音频场景里游刃有余。
7.1 AAudio 架构概览:它到底快在哪?
AAudio 是 Android 在 O 版本(8.0)引入的 C 语言音频 API。它的目标很明确:极致的低延时。说白了,它就是为专业音频应用(比如乐器 App、实时语音、游戏音效)量身定做的。
它的架构核心,我总结为三点:
- 直接与 HAL 通信:AAudio 跳过了 AudioFlinger 的大部分中间层,直接与音频硬件抽象层(HAL)交互。这就像你从走国道改成了上高速,延时自然就降下来了。
- 共享内存队列:AAudio 使用共享内存(Shared Memory)来传输音频数据。应用和 HAL 之间通过环形缓冲区(Ring Buffer)直接读写,省去了数据拷贝的开销。
- 独占模式(Exclusive Mode):AAudio 支持独占模式,这意味着你的音频流可以独享一个音频输出通道,不会被其他 App 打断或混音。这在低延时场景下是杀手锏。
核心要点:AAudio 的低延时,本质上是通过「减少中间层 + 共享内存 + 独占通道」这三板斧实现的。我在项目中测试过,同样的设备,AAudio 的延时可以比 AudioTrack 低 30%-50%。
下面这张图,是我自己画的 AAudio 架构简图。你看一眼就明白了。
我的经验:在实际项目中,我建议你优先尝试独占模式。但要注意,不是所有设备都支持。我在某款低端机上就遇到过独占模式创建失败的情况,所以一定要做好 fallback 到共享模式的准备。
7.2 AAudioStream 的创建与配置:手把手教你搭起来
创建 AAudioStream 其实不复杂,但有几个坑。我带你一步步走。
7.2.1 基本创建流程
核心就三步:
- 构建 AAudioStreamBuilder:设置采样率、通道数、格式等参数。
- 打开 Stream:调用
AAudioStreamBuilder_openStream()。 - 启动 Stream:调用
AAudioStream_requestStart()。
看代码:
// 1. 创建 Builder
AAudioStreamBuilder *builder = nullptr;
AAudio_createStreamBuilder(&builder);
// 2. 配置参数
AAudioStreamBuilder_setSampleRate(builder, 48000);
AAudioStreamBuilder_setChannelCount(builder, 2);
AAudioStreamBuilder_setFormat(builder, AAUDIO_FORMAT_PCM_FLOAT);
AAudioStreamBuilder_setPerformanceMode(builder, AAUDIO_PERFORMANCE_MODE_LOW_LATENCY);
AAudioStreamBuilder_setSharingMode(builder, AAUDIO_SHARING_MODE_EXCLUSIVE);
// 3. 打开 Stream
AAudioStream *stream = nullptr;
AAudioResult result = AAudioStreamBuilder_openStream(builder, &stream);
if (result != AAUDIO_OK) {
// 处理错误,比如回退到共享模式
AAudioStreamBuilder_setSharingMode(builder, AAUDIO_SHARING_MODE_SHARED);
result = AAudioStreamBuilder_openStream(builder, &stream);
}
// 4. 启动
AAudioStream_requestStart(stream);
// 5. 别忘了销毁 Builder
AAudioStreamBuilder_delete(builder);
避坑指南:我曾经在创建 Stream 时忘了检查返回值,结果在低端设备上直接崩溃。记住,AAudioStreamBuilder_openStream() 返回的不是 Stream 指针,而是 AAudioResult。一定要先判断 result,再使用 stream。
7.2.2 关键配置参数详解
这里有几个参数,我建议你重点关注:
| 参数 | 可选值 | 说明 | 我的建议 |
|---|---|---|---|
| PerformanceMode | LOW_LATENCY / NONE / POWER_SAVING | 性能模式,LOW_LATENCY 会优先保证低延时 | 默认用 LOW_LATENCY,除非你特别在意功耗 |
| SharingMode | EXCLUSIVE / SHARED | 独占模式 vs 共享模式 | 优先尝试 EXCLUSIVE,失败后回退到 SHARED |
| Format | PCM_FLOAT / PCM_I16 / PCM_I32 | 音频数据格式 | 推荐 PCM_FLOAT,动态范围大,处理方便 |
| BufferCapacityInFrames | 自定义帧数 | 缓冲区容量,影响延时和稳定性 | 从 128 帧开始试,不够再往上加 |
核心原则:缓冲区越小,延时越低,但越容易卡顿。你需要根据实际设备做平衡。我一般会在 App 启动时做一次「缓冲区探测」,自动选择最优值。
7.2.3 回调模式 vs 阻塞模式
AAudio 支持两种数据读写方式:
- 回调模式(Callback):系统在需要数据时回调你的函数。这是推荐方式,延时最低。
- 阻塞模式(Blocking):你主动调用
AAudioStream_write()或AAudioStream_read()。简单,但延时稍高。
我个人习惯用回调模式。看个例子:
// 回调函数
aaudio_data_callback_result_t audioCallback(
AAudioStream *stream,
void *userData,
void *audioData,
int32_t numFrames) {
// 从你的音频源拷贝数据到 audioData
// 比如从环形缓冲区读取
MyAudioSource *source = (MyAudioSource *)userData;
source->readFrames(audioData, numFrames);
return AAUDIO_CALLBACK_RESULT_CONTINUE;
}
// 设置回调
AAudioStreamBuilder_setDataCallback(builder, audioCallback, this);
小技巧:回调函数里不要做任何阻塞操作,比如加锁、内存分配、日志打印。我见过有人直接在回调里写 logcat,结果音频直接卡成狗。回调里只做数据拷贝,其他事情扔到工作线程去。
7.3 AAudio 与 AudioTrack/AudioRecord 的对比
好,到了大家最关心的对比环节。我直接给你一张表,一目了然。
| 对比维度 | AAudio | AudioTrack / AudioRecord |
|---|---|---|
| 语言 | C/C++(NDK) | Java / C++(JNI) |
| 延时 | 极低(5-15ms) | 较高(20-50ms) |
| 独占模式 | 支持 | 不支持 |
| 回调模式 | 原生支持 | 需要自己封装 |
| 缓冲区控制 | 精细控制(帧级别) | 粗粒度(毫秒级别) |
| 设备兼容性 | Android 8.0+ | 所有版本 |
| 适用场景 | 实时音频、乐器、语音 | 普通播放、录音、流媒体 |
说白了,AAudio 是为「实时」而生的。如果你做的是音乐演奏 App、K 歌、实时语音聊天,AAudio 是首选。但如果你只是做个音乐播放器,AudioTrack 完全够用,没必要折腾。
我的真实案例:之前做一个吉他效果器 App,用 AudioTrack 延时在 40ms 左右,弹起来感觉像在「隔空弹琴」。换成 AAudio 后,延时降到 10ms 以内,体验直接起飞。但要注意,AAudio 在 Android 8.0 以下不可用,所以我在代码里做了版本判断,低版本回退到 AudioTrack。
7.3.1 什么时候用 AAudio?
- 你需要 10ms 以内 的往返延时
- 你的 App 是 实时交互 的(乐器、游戏、语音)
- 你愿意为低延时 牺牲一点兼容性
7.3.2 什么时候用 AudioTrack?
- 你的目标设备 Android 版本低于 8.0
- 你只是做 普通播放(音乐、音效)
- 你不想处理 C/C++ 代码
注意:AAudio 不是万能的。我在某些定制 ROM 上遇到过 AAudio 创建失败的情况。所以,永远要有一个 fallback 方案。我的做法是:先尝试 AAudio,如果失败,自动降级到 AudioTrack,并给用户一个提示。
嗯,这一章的内容就到这里。AAudio 的入门其实不难,关键是理解它的设计哲学——为低延时而生。下一章我们会深入 AAudio 的缓冲区管理和性能调优,到时候你会看到更多实战技巧。