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

通话录制App启动MediaRecorder失败问题排查求助

问题分析与解决方案

核心问题定位

你遇到的崩溃本质是MediaRecorder初始化/启动失败,和文件路径权限、录制触发时机、MediaRecorder状态管理都直接相关,逐个拆解修复:


1. 文件路径权限问题(Android 10+)

你的defineFilePath()使用Environment.getExternalStorageDirectory(),在Android 10(API 29)及以上系统中,该路径的写入权限被严格限制,直接写入会触发IO错误,导致MediaRecorder的prepare()步骤失败。

修复代码:

替换为App专属外部存储目录,无需额外申请全局存储权限:

private fun defineFilePath(context: Context) {
    // 使用App专属外部存储目录,Android 10+无需额外权限
    val directory = File(context.getExternalFilesDir(Environment.DIRECTORY_MUSIC), "Recordings")
    directory.mkdirs()

    // 注意后缀和输出格式匹配:MPEG_4对应.mp4,避免文件无法播放
    val fileName = "recording_${System.currentTimeMillis()}.mp4"
    filePath = File(directory, fileName).absolutePath
    Log.i("anshi","defined file path: $filePath")
}

2. MediaRecorder状态管理错误

你的mediaRecorder是BroadcastReceiver的成员变量,但BroadcastReceiver是短生命周期组件,每次触发onReceive可能生成新实例;且在通话结束的IDLE状态调用release()后,后续触发时已释放的MediaRecorder无法复用。另外,重复调用callRecorder()时未重置MediaRecorder状态,也会引发异常。

修复代码:

改为每次录制前重新初始化MediaRecorder,用Application保存全局引用方便停止:

// 自定义Application类保存当前录制器引用,避免内存泄漏
class MyApplication : Application() {
    var currentRecorder: MediaRecorder? = null
}

在AndroidManifest.xml中注册该Application:

<application
    android:name=".MyApplication"
    ...>
</application>

修改callRecorder()逻辑:

private fun callRecorder(context: Context) {
    defineFilePath(context)

    val mediaRecorder = MediaRecorder().apply {
        setAudioSource(MediaRecorder.AudioSource.VOICE_CALL)
        setOutputFormat(MediaRecorder.OutputFormat.MPEG_4)
        setAudioEncoder(MediaRecorder.AudioEncoder.AAC)
        setOutputFile(filePath)
    }

    try {
        mediaRecorder.prepare()
        mediaRecorder.start()
        // 保存引用到全局,方便通话结束时停止
        (context.applicationContext as MyApplication).currentRecorder = mediaRecorder
        Log.i("anshi","recording started successfully")
    } catch (e: IOException) {
        Log.e("anshi","prepare failed: ${e.message}")
        mediaRecorder.release()
    } catch (e: RuntimeException) {
        Log.e("anshi","start failed: ${e.message}")
        mediaRecorder.release()
    }
}

3. 录制触发时机错误

在EXTRA_STATE_RINGING(来电响铃)状态就启动录制,此时通话尚未接通,VOICE_CALL音频源还未被系统分配,会导致MediaRecorder无法获取音源而启动失败。仅需在EXTRA_STATE_OFFHOOK(通话接通)状态启动录制。

修复onReceive逻辑:

override fun onReceive(context: Context?, intent: Intent?) {
    if(intent?.action == TelephonyManager.ACTION_PHONE_STATE_CHANGED){
        val state = intent.getStringExtra(TelephonyManager.EXTRA_STATE)
        val app = context?.applicationContext as? MyApplication
        when(state){
            TelephonyManager.EXTRA_STATE_RINGING->{
                Log.i("anshi","incoming call ringing, waiting for connection")
                // 此处不启动录制,等待通话接通
            }

            TelephonyManager.EXTRA_STATE_OFFHOOK->{
                Log.i("anshi","call connected, starting recording")
                context?.let { callRecorder(it) }
            }
            TelephonyManager.EXTRA_STATE_IDLE->{
                Log.i("anshi","call ended, stopping recording")
                // 安全停止并释放录制器
                app?.currentRecorder?.let { recorder ->
                    try {
                        recorder.stop()
                    } catch (e: RuntimeException) {
                        // 处理录制未启动就触发停止的异常
                    } finally {
                        recorder.reset()
                        recorder.release()
                        app.currentRecorder = null
                    }
                }
            }
        }
    }
}

4. 权限检查补充

即使在MainActivity申请了权限,也要确保BroadcastReceiver触发时权限仍有效,在callRecorder开头添加权限校验:

private fun callRecorder(context: Context) {
    // 检查录音权限
    if (ContextCompat.checkSelfPermission(context, Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED) {
        Log.e("anshi","RECORD_AUDIO permission not granted")
        return
    }
    // 检查电话状态权限
    if (ContextCompat.checkSelfPermission(context, Manifest.permission.READ_PHONE_STATE) != PackageManager.PERMISSION_GRANTED) {
        Log.e("anshi","READ_PHONE_STATE permission not granted")
        return
    }

    // 后续录制逻辑...
}

最终验证点

  1. 确认AndroidManifest.xml中注册了必要权限:
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="28" />
  1. 测试时确保通话真正接通后再触发录制,避免音源未就绪问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.16 18:50:27