You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

MediaRecorder.stop()抛出IllegalException,点击停止录制致应用崩溃

MediaRecorder录制异常后停止崩溃问题修复

问题根源

  1. 当startRecording方法中prepare()或start()抛出异常时,mediaRecorder对象已被实例化但未进入合法录制状态,此时调用stop()会触发IllegalStateException,直接导致应用崩溃。
  2. getOutputFilePath方法未确保目标目录存在,若AudioRecording文件夹不存在,会引发prepare()失败;且未处理directory为空的情况,会生成无效路径。
  3. 异常发生后未及时清理mediaRecorder实例,残留的无效对象会导致后续操作出错。

修复方案及代码调整

1. 异常时清理MediaRecorder

在startRecording的异常捕获块中,立即释放并置空mediaRecorder,避免后续停止操作触发错误。

2. 确保输出目录存在

在getOutputFilePath中检查并创建目标目录,同时处理directory为空的边界情况。

3. 安全停止录制

新增状态标记追踪录制状态,在stopRecording中增加异常捕获,避免非法状态下的操作引发崩溃。

修改后的完整代码:

private var mediaRecorder: MediaRecorder? = null
// 新增状态标记,精准追踪录制状态
private var isRecording = false

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    binding = ActivityMainBinding.inflate(layoutInflater)
    setContentView(binding.root)

    binding.sendIcon.setOnClickListener {
        startRecording()
    }
    binding.recordingIcon.setOnClickListener {
        stopRecording()
    }
}

private fun startRecording() {
    if (mediaRecorder == null && !isRecording) {
        mediaRecorder = MediaRecorder().apply {
            setAudioSource(MediaRecorder.AudioSource.MIC)
            setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP)
            setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB)
            setAudioSamplingRate(16000)
            setOutputFile(getOutputFilePath())
        }
        try {
            mediaRecorder?.prepare()
            mediaRecorder?.start()
            isRecording = true
        } catch (e: Exception) {
            Log.d("TAG","Recording Exception " + e.localizedMessage)
            // 异常时立即清理资源
            mediaRecorder?.release()
            mediaRecorder = null
            isRecording = false
        }
    }
}

private fun stopRecording() {
    if (mediaRecorder != null && isRecording) {
        try {
            mediaRecorder?.stop()
        } catch (e: IllegalStateException) {
            Log.d("TAG","Stop recording exception: " + e.localizedMessage)
        } finally {
            // 无论是否异常,都确保释放资源并重置状态
            mediaRecorder?.release()
            mediaRecorder = null
            isRecording = false
        }
    }
}

private fun getOutputFilePath(): String {
    val recordingID = "Recording_" + System.currentTimeMillis()
    val directory = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q){
        applicationContext.getExternalFilesDir(Environment.DIRECTORY_MUSIC)
    } else {
        Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MUSIC)
    } ?: run {
        // 处理存储目录为空的情况,降级使用应用内部存储
        filesDir.also { it.mkdirs() }
        return it.absolutePath + "/AudioRecording/" + recordingID + ".3gp"
    }
    val targetDir = File(directory, "AudioRecording")
    // 创建目标目录,不存在则递归新建
    if (!targetDir.exists()) {
        targetDir.mkdirs()
    }
    return File(targetDir, "$recordingID.3gp").absolutePath
}

关键说明

  • isRecording状态标记避免了对未进入录制状态的MediaRecorder执行stop操作。
  • 异常捕获后立即清理资源,防止残留无效对象引发后续错误。
  • 确保输出目录存在,同时兼容不同Android版本的存储策略,处理目录为空的边界场景。
  • stopRecording中的finally块保证无论是否出现异常,都能安全释放MediaRecorder资源。

内容的提问来源于stack exchange,提问作者Taki

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.26 15:47:37