Kotlin ViewModel实现AudioRecord录音和AudioTrack播放
本kotlin工程通过 ViewMode 验证 AudioRecord 录制 PCM 音频并保存到应用自己目录下,通过 AudioTrack 进行播放 PCM 音频文件。为了避免发生内部类内存泄漏,通过在内部类 AudioThread 和AudioTrackThread 前面增加 private 修饰符都定义为静态内部类的方式,避免引用外部类的对象导致内存泄漏。1.AudioViewModelViewModel 的子类,作为录音静态内部类 AudioThread的构造函数参数,用于跟新主线程的UI。更新录音按钮的文本,当点击开始录音后,显示为“停止”,当停止后,更新为“录音”。ViewModel + LiveData 驱动 UIimport androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel class AudioViewModel : ViewModel() { // 定义状态常量 companion object { const val RECORD_STATE_IDLE = 0 const val RECORD_STATE_RECORDING = 1 const val RECORD_STATE_STOPPED = 2 const val RECORD_STATE_ERROR = 3 } // 录音状态,用于更新录音按钮的文本 private val _recordState = MutableLiveDataInt(RECORD_STATE_IDLE) val recordState: LiveDataInt = _recordState // 信息文本 private val _infoText = MutableLiveDataString() val infoText: LiveDataString = _infoText // 更新状态 fun setState(state: Int) { _recordState.postValue(state) } // 更新信息文本 fun setInfoText(text: String) { _infoText.postValue(text) } }2.AudioThread 内部类通过 AudioRecord 实现录制PCM音频文件,保存在应用目录下。无泄漏 + ViewModel + LiveData + 静态内部类AudioViewModel 是 静态内部类 AudioThread 构造函数的参数,用于通知主线程更新录音按钮的文件信息。AudioThread 类的具体代码如下:// 音频录制线程 // 静态内部类 + 传入 ViewModel // 无泄漏 + 能更新 UI private class AudioThread( private val viewModel: AudioViewModel, private val path: String ) : Thread() { private lateinit var record: AudioRecord private var minBufferSize: Int = 0 private var isDone: Boolean = false init { /** * 获取最小 buffer 大小 * 采样率 44100,双声道,采样位数为 16bit */ minBufferSize = AudioRecord.getMinBufferSize( AUDIO_RATE, AudioFormat.CHANNEL_IN_STEREO, AudioFormat.ENCODING_PCM_16BIT ) Log.d(TAG, "minBufferSize = $minBufferSize") } @RequiresPermission(Manifest.permission.RECORD_AUDIO) override fun run() { super.run() try { // 通知开始录音 viewModel.setState(AudioViewModel.RECORD_STATE_RECORDING) // 先创建文件夹 var dir = File(path) if (!dir.exists()) { val result = dir.mkdirs() Log.d(TAG, "create path result: ${result}") } // 创建 pcm 文件 val pcmFile = Utils.getFile(path, "test.pcm") Log.d(TAG, "pcmFile: ${pcmFile.isFile}") /** * 使用 AudioRecord 去录音 */ if (!::record.isInitialized) { record = AudioRecord( MediaRecorder.AudioSource.MIC,