Android音频开发全解析:从架构原理到AudioTrack/AudioRecord实战 1. 从“听个响”到“知其所以然”为什么你需要了解Android Audio如果你是一名Android开发者或者对移动端音频处理感兴趣大概率遇到过这些场景App播放声音时突然被电话打断再回来就哑巴了录音时杂音巨大用户抱怨连连想实现一个简单的混音或变声效果却发现延迟高得离谱声音断断续续甚至只是想调整一下媒体音量却怎么也找不到正确的API最后用了一个被官方标记为“过时”的方法草草了事。这些问题表面上是“功能没实现”根子往往出在对Android Audio系统缺乏一个整体性的认知。Android Audio不是一个简单的MediaPlayer.play()就能概括的它是一个庞大、分层、且与系统深度绑定的复杂框架。很多开发者包括早期的我都曾陷入“面向搜索引擎编程”的困境——遇到问题就搜一段代码贴进去这次能响下次换个场景就崩了。这种“黑盒”式的开发效率低下且隐患重重。这篇内容我想和你系统地梳理一遍Android Audio的核心脉络。目标不是让你成为音频算法专家而是帮你建立起清晰的“地图”。当再遇到音频相关的问题时你能快速定位到是哪个层次应用层、框架层、本地层还是驱动层出了状况知道该去查阅AudioManager、AudioTrack还是AudioRecord的文档甚至能理解/sys/kernel/debug/asoc/下的调试信息在说什么。看完这一篇你未必能立刻写出一个专业的音频App但一定能摆脱对音频开发的恐惧和迷茫拥有独立分析和解决大部分常见音频问题的能力。我们从最基础的“声音怎么从数字信号变成你耳朵听到的声音”这个流程开始。2. Android Audio系统的四层架构一次声音的旅程要理解Android Audio必须把它看作一个完整的处理流水线。一个音频数据从产生到被播放或者从麦克风采集到被应用处理需要穿越四个主要的层次。每一层都有其明确的职责和提供的API/SDK。2.1 应用层你的代码所在之地这是我们最熟悉的一层。你的App在这里通过Android SDK提供的各种API来触发音频行为。这一层的核心是几个关键的类MediaPlayer/SoundPool高级API用于播放压缩的音频文件如MP3、AAC或短促音效。它们封装了编解码、流控等复杂逻辑使用简单但可控性差延迟高。AudioTrack中级API音频播放的“主力军”。它接收原始的PCM音频数据脉冲编码调制简单理解就是未经压缩的、最原始的音频数字信号并负责将其写入音频输出设备。如果你想实现低延迟播放、实时生成音频如合成器、或处理解码后的音频数据就必须使用AudioTrack。AudioRecord中级API音频采集的“核心”。它从音频输入设备通常是麦克风读取原始的PCM数据供你的应用处理或编码。AudioManager系统音频服务的“管家”。它不直接处理音频流但管理着音频的行为策略比如控制音量媒体音量、通话音量、铃声音量等、管理音频焦点决定哪个App能发声、处理设备路由声音是从听筒、扬声器还是蓝牙耳机出来。注意很多新手会混淆MediaPlayer和AudioTrack。你可以这样理解MediaPlayer是一个“全能播放器”它内部包含了一个“解码器”和一个“AudioTrack”。它吃进去的是MP3文件自己解码成PCM再交给内部的AudioTrack去播放。而AudioTrack是一个“纯粹的喇叭驱动”它只认PCM数据。所以当你需要更底层的控制时就得直接和AudioTrack打交道。2.2 框架层策略与路由的中枢应用层的API调用最终都会汇集到框架层的AudioService一个系统服务。这里是Android Audio的“大脑”负责执行全局的音频策略。音频焦点管理这是避免多个App同时“抢喇叭”混乱局面的核心机制。当你的App要播放声音时应该通过AudioManager.requestAudioFocus()来申请焦点。系统会根据焦点策略比如音乐播放器申请焦点后导航语音可以“短暂闪避”但不会停止来协调所有App的行为。不遵守焦点规则的App会被视为“不友好”用户体验很差。音量曲线管理你按手机音量键调整的并不是一个简单的线性值。框架层定义了不同音频类型媒体、通话、警报等在不同设备扬声器、听筒、蓝牙上独特的音量曲线。AudioManager的setStreamVolume()就是在和这套策略交互。设备路由决策当用户插入耳机或连接蓝牙音箱时AudioService会收到通知并决定将后续的音频流自动路由到新设备。应用可以通过AudioManager查询当前音频路由信息。2.3 本地层与HAL与硬件对话的翻译官框架层之下是本地层Native Layer主要由C/C实现包含了核心的音频引擎。而硬件抽象层是本地层与内核驱动之间的桥梁。AudioFlinger这是Android音频系统的“心脏”一个运行在mediaserver进程中的本地服务。所有AudioTrack和AudioRecord最终都要和它通信。它负责混音将系统中所有正在播放的音频流比如后台音乐和游戏音效混合成一个统一的PCM流。设备管理管理实际的音频硬件设备如扬声器、麦克风阵列。效果器处理在音频数据写入硬件前可以施加全局的音效如均衡器、重低音增强等通过AudioEffectAPI控制。AudioPolicyService音频策略服务的本地实现与框架层的AudioService协同工作制定更底层的路由和策略。HAL硬件抽象层。它定义了标准接口让AudioFlinger可以用统一的方式命令不同厂商如高通、联发科的音频硬件芯片。厂商需要实现这些接口。这也是为什么你在搜索问题时经常会看到/sys/kernel/debug/asoc/这样的路径这是Linux内核中ALSA高级Linux声音架构驱动提供的调试信息接口用于查看底层编解码器、声卡的状态通常由芯片厂商的HAL实现或驱动暴露出来对应用层开发者来说这是在排查极端底层硬件/驱动问题时才会用到的“显微镜”。2.4 驱动层物理世界的开关最底层是Linux内核中的音频驱动如ALSA驱动它直接操作音频编解码器芯片控制数模转换器将数字PCM信号转换成模拟电信号推动扬声器或者将麦克风产生的模拟信号转换成数字PCM信号。理解了这四层架构我们就能像侦探一样排查问题如果声音播放不出来可以先检查自己的AudioTrack配置和写入数据是否正确应用层再检查是否申请了音频焦点或被其他焦点策略打断框架层然后可以尝试查看Logcat中AudioFlinger相关的日志本地层对于特定机型的问题可能就需要考虑HAL或驱动的兼容性问题了。3. 核心API深度剖析AudioTrack与AudioRecord实战了解了架构我们深入到最常用、也最易出问题的两个核心类AudioTrack和AudioRecord。用好它们是进行任何高级音频处理的基础。3.1 AudioTrack精准投放音频数据AudioTrack的工作模式就像一个“生产者-消费者”模型。你的App是生产者不断向一个缓冲区写入PCM数据AudioTrack和AudioFlinger是消费者从缓冲区另一端读取数据送去播放。关键构造参数解析流类型 vs 使用模式streamType如STREAM_MUSIC,STREAM_ALARM。这主要影响音频焦点和音量控制归属。播放背景音乐就用STREAM_MUSIC。mode这是核心。MODE_STATIC一次性将所有音频数据加载到缓冲区。适合短促、重复播放的音效类似SoundPool延迟极低。MODE_STREAM需要你不断地、分批次向AudioTrack写入数据。适合播放长音频或实时生成音频。这是我们最常用的模式。音频属性配置这是PCM数据的“身份证”必须与你的数据完全匹配否则会产生杂音或速度异常。sampleRateInHz采样率。如44100HzCD音质、48000Hz。必须与你的PCM数据源一致。channelConfig声道配置。如CHANNEL_OUT_MONO单声道CHANNEL_OUT_STEREO立体声。audioFormat采样精度。如ENCODING_PCM_16BIT每个采样点用16位即2字节表示ENCODING_PCM_8BIT或浮点型的ENCODING_PCM_FLOAT。同样必须与数据一致。一个标准的流模式播放流程// 1. 计算最小缓冲区大小 int minBufferSize AudioTrack.getMinBufferSize(44100, AudioFormat.CHANNEL_OUT_STEREO, AudioFormat.ENCODING_PCM_16BIT); // 2. 创建AudioTrack实例 AudioTrack audioTrack new AudioTrack( new AudioAttributes.Builder() .setUsage(AudioAttributes.USAGE_MEDIA) .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC) .build(), new AudioFormat.Builder() .setSampleRate(44100) .setChannelMask(AudioFormat.CHANNEL_OUT_STEREO) .setEncoding(AudioFormat.ENCODING_PCM_16BIT) .build(), minBufferSize * 2, // 通常设置为最小值的2-4倍平衡延迟和稳定性 AudioTrack.MODE_STREAM, AudioManager.AUDIO_SESSION_ID_GENERATE); // 3. 开始播放 audioTrack.play(); // 4. 在一个独立线程中不断写入PCM数据 new Thread(() - { byte[] audioData ... // 从文件、网络或实时生成你的PCM数据 int written 0; while (isPlaying written 0) { written audioTrack.write(audioData, 0, audioData.length); // 注意write方法是阻塞的直到数据被成功写入内部缓冲区 // 你需要根据write的返回值控制数据生产和写入的节奏避免缓冲区欠载Underrun导致卡顿 } }).start(); // 5. 停止和释放 audioTrack.stop(); audioTrack.release(); // 非常重要释放 native 资源实操心得write方法的阻塞特性是一把双刃剑。如果数据生产太慢比如从网络读取会导致缓冲区空产生“卡顿”或“噼啪”声欠载。如果生产太快主线程或写线程可能被长时间阻塞。常见的做法是使用双缓冲区或环形缓冲区一个线程负责生产数据填充缓冲区A另一个线程负责从缓冲区B写入AudioTrack两者交替进行。此外getMinBufferSize返回的是“能保证正常播放的最小缓冲区”实际使用中为了更平滑的播放特别是对付系统调度带来的延迟建议设置得更大一些2-4倍。3.2 AudioRecord高质量声音采集AudioRecord是AudioTrack的镜像它从音频输入设备读取PCM数据。关键构造参数与流程其参数与AudioTrack类似但方向是输入。特别注意audioSource参数它定义了音频的来源如MediaRecorder.AudioSource.MIC主麦克风、VOICE_COMMUNICATION用于通话的麦克风通常带有回声消除等处理。// 1. 计算最小缓冲区大小 int minBufferSize AudioRecord.getMinBufferSize(44100, AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT); // 2. 创建AudioRecord实例 AudioRecord audioRecord new AudioRecord( MediaRecorder.AudioSource.MIC, 44100, AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT, minBufferSize * 2); // 3. 开始录制 audioRecord.startRecording(); // 4. 在一个独立线程中不断读取PCM数据 new Thread(() - { byte[] buffer new byte[minBufferSize]; while (isRecording) { int read audioRecord.read(buffer, 0, buffer.length); if (read 0) { // 处理buffer中的数据保存到文件、上传网络或实时分析 processAudioData(buffer, read); } } }).start(); // 5. 停止和释放 audioRecord.stop(); audioRecord.release();常见坑点与排查权限问题别忘了在AndroidManifest.xml中声明RECORD_AUDIO权限并且在Android 6.0上需要运行时申请。无声音或杂音首先检查audioSource是否选对。其次确认采样率、声道、格式与设备支持的能力匹配。可以通过AudioRecord.getMinBufferSize()的返回值判断如果返回ERROR_BAD_VALUE或ERROR就说明参数组合不被支持。最后确保read操作在一个有Looper的线程中持续进行否则缓冲区满了之后数据会丢失。延迟问题AudioRecord本身延迟较低但如果你read之后进行复杂的处理如软件编码累积延迟就会很高。对于实时语音通话需要考虑使用AudioRecord的低延迟模式API级别23或直接使用WebRTC等更专业的库。4. 音频焦点与设备管理让你的App成为“好公民”一个音频App如果只关心自己发声不顾及其他App和系统状态会非常惹人厌。这就需要AudioManager来协调。4.1 音频焦点学会“举手发言”当你的App要播放声音时应该先申请焦点。这相当于在会议上举手“我要发言了”。系统会根据你申请的焦点类型和其他App的焦点状态决定如何处理。AudioManager audioManager (AudioManager) getSystemService(Context.AUDIO_SERVICE); AudioFocusRequest focusRequest new AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN) .setAudioAttributes(new AudioAttributes.Builder() .setUsage(AudioAttributes.USAGE_MEDIA) .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC) .build()) .setAcceptsDelayedFocusGain(true) // 是否接受延迟获得焦点 .setOnAudioFocusChangeListener(new AudioManager.OnAudioFocusChangeListener() { Override public void onAudioFocusChange(int focusChange) { switch (focusChange) { case AudioManager.AUDIOFOCUS_GAIN: // 重新获得焦点恢复播放并可能提高音量 mediaPlayer.start(); mediaPlayer.setVolume(1.0f, 1.0f); break; case AudioManager.AUDIOFOCUS_LOSS: // 长期失去焦点停止播放并释放资源 mediaPlayer.stop(); // 可以在这里释放MediaPlayer break; case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT: // 短暂失去焦点如来电暂停播放 mediaPlayer.pause(); break; case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK: // 短暂失去焦点但可以降低音量如导航语音播报 mediaPlayer.setVolume(0.2f, 0.2f); // 降低音量 break; } } }) .build(); int result audioManager.requestAudioFocus(focusRequest); if (result AudioManager.AUDIOFOCUS_REQUEST_GRANTED) { // 成功获得焦点开始播放 mediaPlayer.start(); } else if (result AudioManager.AUDIOFOCUS_REQUEST_DELAYED) { // 焦点被延迟授予需要等待回调 }焦点类型详解AUDIOFOCUS_GAIN长期焦点用于音乐播放、视频播放等。获得此焦点后之前持有焦点的App会收到AUDIOFOCUS_LOSS。AUDIOFOCUS_GAIN_TRANSIENT短暂焦点用于提示音、语音识别。其他App会收到AUDIOFOCUS_LOSS_TRANSIENT。AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK短暂焦点且允许其他App“闪避”降低音量。这是最友好的方式适用于导航播报、通知音等。其他App会收到AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK并应降低音量。4.2 设备路由与监听用户可能随时插入耳机或连接蓝牙设备你的App应该能优雅地处理这些变化。查询当前音频设备AudioManager audioManager (AudioManager) getSystemService(Context.AUDIO_SERVICE); AudioDeviceInfo[] devices audioManager.getDevices(AudioManager.GET_DEVICES_OUTPUTS); for (AudioDeviceInfo device : devices) { int type device.getType(); if (type AudioDeviceInfo.TYPE_BLUETOOTH_A2DP) { Log.d(Audio, 当前输出设备是蓝牙A2DP); } else if (type AudioDeviceInfo.TYPE_WIRED_HEADPHONES) { Log.d(Audio, 当前输出设备是有线耳机); } // ... 其他类型 }监听设备变化// 注册一个广播接收器 private final BroadcastReceiver mAudioDeviceReceiver new BroadcastReceiver() { Override public void onReceive(Context context, Intent intent) { if (AudioManager.ACTION_HEADSET_PLUG.equals(intent.getAction())) { int state intent.getIntExtra(state, -1); if (state 1) { Log.d(Audio, 耳机已插入); // 可以切换到更适合耳机的音效或均衡器 } else if (state 0) { Log.d(Audio, 耳机已拔出); // 切回扬声器模式注意降低音量避免公放尴尬 audioManager.setStreamVolume(AudioManager.STREAM_MUSIC, audioManager.getStreamVolume(AudioManager.STREAM_MUSIC) / 2, // 示例音量减半 0); } } // 也可以监听蓝牙连接状态 ACTION_ACL_CONNECTED/DISCONNECTED } }; // 在Activity或Service中注册 IntentFilter filter new IntentFilter(AudioManager.ACTION_HEADSET_PLUG); registerReceiver(mAudioDeviceReceiver, filter);重要提示从Android SAPI 31开始ACTION_HEADSET_PLUG广播已被废弃。官方推荐使用AudioManager的addOnAudioDevicesChangedListener方法来监听设备变化这是一个更现代、更精准的方式。处理好音频焦点和设备路由你的App就能在复杂的系统音频环境中表现得体不会在用户接电话时还大声播放音乐也不会在拔出耳机后突然公放从而提供专业、可靠的用户体验。5. 进阶话题与性能调优掌握了基础架构和核心API后我们可以探讨一些更深入的话题这些往往是实现高性能、低延迟音频应用的关键。5.1 低延迟音频游戏与音乐创作的基石对于实时性要求极高的场景如乐器App、DJ应用、FPS游戏音效普通的AudioTrack和AudioRecord可能因为系统缓冲和调度带来的延迟通常超过100毫秒而无法满足需求。Android从OreoAPI 26开始引入了低延迟音频路径。核心AAudio APIAAudio是Google推出的一个旨在提供高性能、低延迟音频的C APIJava层有封装。它比AudioTrack/AudioRecord更接近硬件绕过了AudioFlinger的混音器在特定模式下延迟可以降到10毫秒以内。关键概念性能模式AAUDIO_PERFORMANCE_MODE_NONE,LOW_LATENCY,POWER_SAVING。低延迟模式会牺牲一些功耗和稳定性来换取更快的响应。共享模式SHARED共享经过混音延迟较高但更稳定和EXCLUSIVE独占可能绕过混音器直接访问硬件延迟最低但需要硬件支持且可能被系统抢占。使用建议如果你的App目标API在26以上且对延迟极其敏感应优先考虑AAudio。在创建流时请求LOW_LATENCY性能模式和EXCLUSIVE共享模式。必须处理好数据回调确保在回调函数中快速填充或消费音频数据任何阻塞都会导致“欠载”或“过载”产生爆音。由于AAudio主要是C/C API在Java层使用相对复杂通常用于NDK开发。但对于追求极致性能的场景它是必选项。5.2 音频效果处理从系统均衡器到自定义算法Android提供了AudioEffectAPI允许你对通过AudioTrack播放或AudioRecord采集的音频流施加实时效果。系统内置效果器Equalizer均衡器调节不同频段的增益。BassBoost低音增强。Virtualizer虚拟环绕声。PresetReverb预设混响。EnvironmentalReverb环境混响更复杂的参数控制。使用示例为AudioTrack附加均衡器// 首先创建并启动AudioTrack AudioTrack track ...; track.play(); // 创建均衡器效果并关联到AudioTrack的音频会话ID int audioSessionId track.getAudioSessionId(); Equalizer equalizer new Equalizer(0, audioSessionId); equalizer.setEnabled(true); // 启用 // 获取均衡器支持的频段数 short bands equalizer.getNumberOfBands(); // 设置每个频段的增益范围通常是-1500到1500毫分贝 equalizer.setBandLevel((short)0, (short)500); // 提升第一个频段 // 使用完毕后释放 equalizer.release();自定义音频处理如果系统效果器不满足需求你需要自己处理PCM数据。这通常在AudioTrack.write()之前或AudioRecord.read()之后进行。例如实现一个简单的音量调节// 假设audioData是16位PCM的byte数组 public void adjustVolume(byte[] audioData, float volume) { // volume: 0.0f ~ 1.0f for (int i 0; i audioData.length; i 2) { // 16位 2字节 // 将两个字节组合成一个short小端序 short sample (short)((audioData[i] 0xff) | (audioData[i1] 8)); // 应用音量增益 sample (short)(sample * volume); // 写回字节数组 audioData[i] (byte)(sample 0xff); audioData[i1] (byte)((sample 8) 0xff); } }更复杂的处理如降噪、回声消除、变声通常需要借助第三方NDK库如WebRTC的音频处理模块或专门的音频DSP芯片。5.3 常见性能问题与调试技巧音频卡顿/爆音根因通常是“欠载”。AudioTrack的播放缓冲区空了但你的App没有及时供给新的数据。排查检查数据生产线程的优先级是否足够高是否被其他操作阻塞。增大AudioTrack的缓冲区大小bufferSizeInBytes。使用MODE_STATIC模式播放短音效。在AudioTrack的构造函数中尝试使用AudioAttributes.Builder.setFlags(AudioAttributes.FLAG_LOW_LATENCY)如果支持。查看Logcat中是否有AudioTrack或AudioFlinger的“underrun”相关警告。录音延迟高根因AudioRecord.read()之后的数据处理耗时太长或者AudioRecord缓冲区设置太小。排查将read操作和后续处理如编码、网络发送放在不同的线程使用生产者-消费者模型。确保处理算法高效避免在音频线程进行复杂计算或I/O操作。尝试使用AudioRecord的低延迟模式AudioFormat.Builder.setPerformanceMode。功耗过高根因音频线程持续高负载运行阻止CPU进入休眠状态。排查使用WakeLock时要谨慎只在必要时持有。对于后台播放确保使用了正确的AudioAttributes如USAGE_MEDIA和AudioFocus系统可能会进行优化。使用Android Studio的Profiler工具监控CPU使用率和WakeLock持有情况。利用调试工具Logcat过滤AudioFlinger、AudioTrack、AudioRecord等标签查看内部状态和错误信息。dumpsys media.audio_policy和dumpsys media.audio_flinger在ADB Shell中执行这些命令可以获取系统音频策略和Flinger的详细状态信息包括所有活跃的音频流、设备路由、HAL模块状态等。信息量巨大是排查复杂问题的利器。厂商调试接口如之前提到的cat /sys/kernel/debug/asoc/*命令可以查看底层ALSA驱动和编解码器的状态如DAPM电源状态、时钟配置。这通常需要设备有root权限并且信息格式因芯片平台如高通、联发科而异一般在驱动层或HAL层出现问题时由芯片厂商的工程师使用。6. 实战构建一个简单的低延迟音频循环器让我们把上面的知识串联起来实现一个简单的概念验证应用一个音频循环器。它通过AudioRecord实时采集麦克风声音经过一个简单的延迟效果处理将当前声音与0.3秒前的声音混合再通过AudioTrack实时播放出来形成回声效果。这个例子涵盖了采集、处理、播放的全链路。核心步骤配置参数与初始化private static final int SAMPLE_RATE 44100; private static final int CHANNEL_CONFIG_IN AudioFormat.CHANNEL_IN_MONO; private static final int CHANNEL_CONFIG_OUT AudioFormat.CHANNEL_OUT_MONO; private static final int AUDIO_FORMAT AudioFormat.ENCODING_PCM_16BIT; private static final int DELAY_MS 300; // 300毫秒延迟 private static final int DELAY_SAMPLES (int)(SAMPLE_RATE * DELAY_MS / 1000.0f); // 计算延迟对应的采样点数 private AudioRecord audioRecord; private AudioTrack audioTrack; private short[] delayBuffer; // 用于存储历史数据的环形缓冲区 private int delayBufferIndex 0; private boolean isRunning false;创建AudioRecord和AudioTrackprivate void initAudio() { int minBufferSize Math.max( AudioRecord.getMinBufferSize(SAMPLE_RATE, CHANNEL_CONFIG_IN, AUDIO_FORMAT), AudioTrack.getMinBufferSize(SAMPLE_RATE, CHANNEL_CONFIG_OUT, AUDIO_FORMAT) ); // 为了低延迟我们使用较小的缓冲区但实际项目中需要根据设备能力调整 int bufferSize minBufferSize * 2; audioRecord new AudioRecord( MediaRecorder.AudioSource.MIC, SAMPLE_RATE, CHANNEL_CONFIG_IN, AUDIO_FORMAT, bufferSize); audioTrack new AudioTrack( new AudioAttributes.Builder() .setUsage(AudioAttributes.USAGE_MEDIA) .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC) .build(), new AudioFormat.Builder() .setSampleRate(SAMPLE_RATE) .setChannelMask(CHANNEL_CONFIG_OUT) .setEncoding(AUDIO_FORMAT) .build(), bufferSize, AudioTrack.MODE_STREAM, AudioManager.AUDIO_SESSION_ID_GENERATE); // 初始化延迟缓冲区 delayBuffer new short[DELAY_SAMPLES]; Arrays.fill(delayBuffer, (short)0); }实现音频处理线程private void startLoop() { if (isRunning) return; isRunning true; audioRecord.startRecording(); audioTrack.play(); new Thread(() - { short[] buffer new short[1024]; // 每次处理的采样帧数 while (isRunning) { // 1. 采集 int read audioRecord.read(buffer, 0, buffer.length); if (read 0) { // 2. 处理应用延迟效果当前样本 衰减后的历史样本 for (int i 0; i read; i) { short currentSample buffer[i]; short delayedSample delayBuffer[delayBufferIndex]; // 混合原始音 0.5倍的延迟音 short mixedSample (short)(currentSample (delayedSample * 0.5f)); // 防止溢出削波 if (mixedSample Short.MAX_VALUE) mixedSample Short.MAX_VALUE; if (mixedSample Short.MIN_VALUE) mixedSample Short.MIN_VALUE; // 将当前样本存入延迟缓冲区供下次使用 delayBuffer[delayBufferIndex] currentSample; delayBufferIndex (delayBufferIndex 1) % DELAY_SAMPLES; // 更新输出缓冲区 buffer[i] mixedSample; } // 3. 播放 audioTrack.write(buffer, 0, read, AudioTrack.WRITE_BLOCKING); } } }).start(); } private void stopLoop() { isRunning false; if (audioRecord ! null) { audioRecord.stop(); } if (audioTrack ! null) { audioTrack.stop(); } }释放资源Override protected void onDestroy() { super.onDestroy(); stopLoop(); if (audioRecord ! null) { audioRecord.release(); audioRecord null; } if (audioTrack ! null) { audioTrack.release(); audioTrack null; } }这个例子中你会遇到的典型问题与优化方向延迟与同步采集、处理、播放三个环节在同一个线程中串行执行任何一环慢了都会导致整体延迟增加甚至缓冲区欠载。优化方案是使用多线程和环形缓冲区将采集、处理、播放流水线化。音质与削波简单的加法混合很容易导致数值溢出削波产生刺耳的失真。我们做了简单的钳位处理但更好的方法是进行动态范围控制或使用浮点数运算。功耗这个线程会持续全速运行非常耗电。在实际应用中需要精细控制线程的唤醒间隔或者在无声音输入时进入休眠。设备兼容性AudioRecord的read方法和AudioTrack的write方法在某些设备上的实际延迟可能远高于预期。对于真正追求低延迟的应用需要检测设备是否支持低延迟路径并考虑使用AAudio API。通过这个实战例子你应该能更直观地理解Android Audio API是如何协同工作的以及在实际编码中需要考虑的种种细节。从架构原理到API使用再到问题排查和性能优化这条链路走通之后大部分Android音频开发任务对你来说都将不再是黑盒。剩下的就是根据具体需求深入某个细分领域如音频编解码、3D音效、语音识别集成等去积累更专门的经验了。