drain
当上层应用(如播放器)检测到音频文件已到达末尾(EOF)时,它会向 AudioTrack 发出停止指令。Framework 会调用 HAL 的 out_drain 接口,要求硬件把缓冲区里剩余的字节"消化"完。
drain 的两种模式
DRAIN_ALL (全部排空):等待当前缓冲区中的**所有**音频数据播放完毕后再返回。这是最常用的模式,用于正常的歌曲切换或结束。
DRAIN_EARLY_NOTIFY (提前通知):在数据即将播放完(例如还剩最后一点)时就发出回调,以便上层应用能提前准备下一首歌的数据,实现"无缝切换"。
为什么需要 drain?
如果不实现 drain 直接调用 stop 或 flush,会发生什么?
音频截断:你会听到歌曲最后几百毫秒或 1-2 秒的内容突然消失(因为这些数据还在硬件 Ring Buffer 里没播出来)。
爆音/咔哒声:直接切断模拟信号可能导致硬件输出电平瞬间归零,产生杂音。
flash
flush(冲洗/清空)操作的主要目的是立即停止播放并丢弃缓冲区中所有待播放的数据。这通常发生在用户切换歌曲、点击停止或在视频播放中执行快进/快退(Seek)操作时。
与 drain(排空)不同,flush 是"暴力"的------它不要求把剩下的音频播完,而是要求硬件立刻"清零"。
|------|--------------------|--------------------|
| 特性 | drain | flush |
| 主要目的 | 优雅地播完最后一点声音 | 立即丢弃所有声音,准备新操作 |
| 触发场景 | 歌曲自然播放结束 | 用户切歌、快进、停止 |
| 数据处理 | 等待硬件 Buffer 播放结束 | 硬件 Buffer 里的数据直接扔掉 |
| 耗时 | 较长(取决于 Buffer 大小) | 极短(几乎瞬间完成) |
| 回调 | 完成后发送 onDrainReady | 完成后不需要特殊 Ready 回调 |
为什么 Non-blocking 模式下 flush 很重要?
在非阻塞模式下,write 调用可能会因为 Buffer 满而返回 0。如果不执行 flush 就进行 Seek 操作,那么当 write 恢复时,它会继续写入旧位置之后的残余数据,导致用户在快进后听到一小段快进前的"声音残影"。执行 flush 可以确保硬件和 HAL 软件层面的"水管"被完全排空。