Android音频录制:MediaRecorder弃用构造函数及报错解决咨询
问题分析与解决方案
一、报错原因
带Context参数的MediaRecorder(Context)构造函数是**Android 12(API 31)**才新增的API,当应用运行在低于API31的设备上时,系统内置的MediaRecorder类不存在该方法,因此会抛出NoSuchMethodError。
二、非弃用的MediaRecorder初始化方式
需根据系统版本做兼容处理:API31及以上使用带Context的构造,低于API31的版本继续使用无参构造(虽在API31+标记为弃用,但低版本仍可正常运行)。
封装初始化方法
private fun createRecorder(context: Context): MediaRecorder { return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { MediaRecorder(context) } else { // 用注解抑制API31+的弃用警告 @Suppress("DEPRECATION") MediaRecorder() } }
在startRecording()中替换原实例化代码:
recorder = createRecorder(this)
三、现有代码的优化点
- 完善异常处理
原代码空catch块会隐藏调试信息,至少添加异常日志:
try { prepare() } catch (e: IOException) { e.printStackTrace() Toast.makeText(this@MainActivity, "录制初始化失败", Toast.LENGTH_SHORT).show() return }
- 修复SimpleDateFormat兼容性
android.icu.text.SimpleDateFormat仅API24+可用,改用兼容全版本的java.text.SimpleDateFormat,同时修正格式字符串(月份用MM而非mm,避免和分钟混淆):
import java.text.SimpleDateFormat // ... val simpleDateFormat = SimpleDateFormat("yyyy.MM.dd.HH.mm.ss", Locale.getDefault()) val date: String = simpleDateFormat.format(Date())
- 优化权限请求逻辑
用户拒绝权限后,引导其理解权限必要性,而非重复请求:
if (!permissionGranted) { if (ActivityCompat.shouldShowRequestPermissionRationale(this, permission[0])) { Toast.makeText(this, "需要录音权限才能录制音频", Toast.LENGTH_SHORT).show() } ActivityCompat.requestPermissions(this, permission, REQUEST_CODE) return }
- 释放MediaRecorder资源
Activity销毁时释放资源,避免内存泄漏和系统资源占用:
override fun onDestroy() { super.onDestroy() if (::recorder.isInitialized) { runCatching { if (isRecording || isPaused) { recorder.stop() } } recorder.release() } }
四、修改后的关键代码片段
// 替换原startRecording方法 @RequiresApi(Build.VERSION_CODES.S) private fun startRecording() { if (!permissionGranted) { if (ActivityCompat.shouldShowRequestPermissionRationale(this, permission[0])) { Toast.makeText(this, "需要录音权限才能录制音频", Toast.LENGTH_SHORT).show() } ActivityCompat.requestPermissions(this, permission, REQUEST_CODE) return } recorder = createRecorder(this) dirPath = "${externalCacheDir?.absolutePath}/" val simpleDateFormat = SimpleDateFormat("yyyy.MM.dd.HH.mm.ss", Locale.getDefault()) val date: String = simpleDateFormat.format(Date()) filename = "audio_record_$date" recorder.apply { setAudioSource(MediaRecorder.AudioSource.MIC) setOutputFormat(MediaRecorder.OutputFormat.MPEG_4) setAudioEncoder(MediaRecorder.AudioEncoder.AAC) setOutputFile("$dirPath$filename.mp3") try { prepare() start() } catch (e: IOException) { e.printStackTrace() Toast.makeText(this@MainActivity, "录制初始化失败", Toast.LENGTH_SHORT).show() return } } btnRecord.setImageResource(R.drawable.ic_pause) isRecording = true isPaused = false } private fun createRecorder(context: Context): MediaRecorder { return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { MediaRecorder(context) } else { @Suppress("DEPRECATION") MediaRecorder() } } override fun onDestroy() { super.onDestroy() if (::recorder.isInitialized) { runCatching { if (isRecording || isPaused) { recorder.stop() } } recorder.release() } }
内容的提问来源于stack exchange,提问作者Ahmed Hossam
相关产品推荐
相关产品推荐

