安卓开发过程中一旦开始和硬件打交道,以及涉及到一定的native代码之后,各种闪退就开始浮出水面了,声音录制和播放当然不例外。本文总结了YOLO安卓客户端大半年来的安卓音频实践,整理出一套系统API的封装,命名为 RxAndroidAudio 。
安卓平台和声音录制与播放相关的主要是4个类:MediaRecorder,MediaPlayer,AudioRecord和AudioTrack。
MediaRecorder可以录制视频和音频到文件,MediaPlayer可以播放视频和音频文件,AudioRecord可以提供接口读取音频流数据(byte数组或者short数组),AudioTrack提供接口用于播放音频流数据。
小结一下,其中MediaRecorder和AudioRecord用于声音录制,MediaPlayer和AudioTrack用于声音播放。AudioRecord和AudioTrack用于操作音频流数据,操作对象是byte数组(或者short数组),而MediaRecorder和MediaPlayer提供了经过更高层抽象和封装接口,直接对文件进行操作,而且他俩功能更丰富,同时支持音频和视频。
本文会涉及到部分关于声音录制和播放更底层的实现,详细的分析并不是本文的内容范围了,此外本文主要目标并不是声音录制和播放的使用教程,本文主要关注的是上述4个类的使用过程中需要注意的地方。
mRecorder.prepare()
调用需要 捕获 IOException
和 RuntimeException
,注意需要捕获 RuntimeException
而不是 IllegalStateException
,尽管Java doc中只声明了会抛出IllegalStateException,但是查看 jni层代码 可以看到,prepare的调用也是可能会触发RuntimeException的; mRecorder.start()
, mRecorder.stop()
, mRecorder.reset()
调用也需要捕获 RuntimeException
,理由同上; 需要保证不会对jni层进行多线程的调用,以免出现下面这样的“静默闪退”( 参考资料 ),RxAndroidAudio通过 单例 和 synchronized方法 来保证这一点:
A/libc: Fatal signal 11 (SIGSEGV) at 0x00000010 (code=1), thread 9302 (RxComputationTh)
mPlayer.prepare()
,而资源文件id方式不需要; MediaPlayer.OnCompletionListener
的 onCompletion
回调中,需要 延迟一定时间再释放MediaPlayer ,否则可能导致下次紧接着的播放无法成功(静默失败,不会抛出异常),原因还需要继续寻找; mAudioRecord.read
的返回值需要进行错误检查; mAudioRecord.read
传入的参数类型需要进行区分 , ENCODING_PCM_16BIT
格式的录音需要传入short数组, ENCODING_PCM_8BIT
格式的录音需要传入byte数组,尽管在 jni层的实现 都是一样的,但是 Java doc 明确说明了这一注意事项,理应遵循; ExecutorService
来执行异步任务(从AudioRecord中循环读取数据); Reactive!
RxAndroidAudio之所以叫Rx,就是因为它尽可能的提供了Reactive的API。
RxAudioPlayer播放声音文件: