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

Android Studio(Kotlin)中MediaRecorder setOutputFile参数错误问题排查

问题原因

你遇到的setOutputFile()报错,核心问题是参数类型不匹配:

  • shush1是raw资源的整数ID(Int类型),但MediaRecorder.setOutputFile()只接受File、FileDescriptor或文件路径字符串(String)这三种参数。
  • 另外,raw目录下的资源是打包进APK的只读文件,根本无法直接写入修改,所以你不能直接把录制内容写到原音效文件里,必须指定一个可写入的外部存储路径。
修复方案

1. 添加必要权限

在AndroidManifest.xml中声明录制和存储权限:

<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="32" />
<uses-permission android:name="android.permission.READ_MEDIA_AUDIO" android:minSdkVersion="33" />

Android 13及以上需要用READ_MEDIA_AUDIO替代旧的存储权限,并且要在运行时动态申请这些权限。

2. 修改录制逻辑,指定可写输出文件

把setOutputFile(shush1)替换为指向外部存储的文件路径,比如APP专属的音频目录:

// 获取APP外部存储的音频目录,创建录制文件
val outputDir = mContext.getExternalFilesDir(Environment.DIRECTORY_AUDIO)
outputDir?.mkdirs() // 确保目录存在
val outputFile = File(outputDir, "shush_recorded.mp3")
setOutputFile(outputFile.absolutePath)

3. 修正后的完整录制按钮逻辑

// 定义权限请求码(类级别常量)
private const val REQUEST_PERMISSIONS_CODE = 1001

// 录制按钮点击事件
IconButton(onClick = {
    if (!isRecording) {
        // 停止并释放原有播放器
        mediaPlayer?.let {
            if (it.isPlaying) it.stop()
            it.release()
        }
        // 检查权限
        val hasRecordPerm = ContextCompat.checkSelfPermission(mContext, Manifest.permission.RECORD_AUDIO) == PackageManager.PERMISSION_GRANTED
        val hasStoragePerm = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
            ContextCompat.checkSelfPermission(mContext, Manifest.permission.READ_MEDIA_AUDIO) == PackageManager.PERMISSION_GRANTED
        } else {
            ContextCompat.checkSelfPermission(mContext, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED
        }

        if (hasRecordPerm && hasStoragePerm) {
            val outputDir = mContext.getExternalFilesDir(Environment.DIRECTORY_AUDIO)
            outputDir?.mkdirs()
            val outputFile = File(outputDir, "shush_recorded.mp3")
            
            recorder = MediaRecorder().apply {
                setAudioSource(MediaRecorder.AudioSource.MIC)
                setOutputFormat(MediaRecorder.OutputFormat.MPEG_4)
                setOutputFile(outputFile.absolutePath)
                setAudioEncoder(MediaRecorder.AudioEncoder.AAC) // 推荐用AAC音质更好
                try {
                    prepare()
                    start()
                } catch (e: IOException) {
                    e.printStackTrace()
                }
            }
        } else {
            // 动态请求权限
            ActivityCompat.requestPermissions(
                mContext as Activity,
                arrayOf(
                    Manifest.permission.RECORD_AUDIO,
                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) Manifest.permission.READ_MEDIA_AUDIO else Manifest.permission.WRITE_EXTERNAL_STORAGE
                ),
                REQUEST_PERMISSIONS_CODE
            )
        }
    } else {
        // 停止录制并释放资源
        recorder?.apply {
            stop()
            reset()
            release()
        }
        recorder = null
        
        // 录制完成后,切换到新录制的音频播放
        val outputFile = File(mContext.getExternalFilesDir(Environment.DIRECTORY_AUDIO), "shush_recorded.mp3")
        mediaPlayer = if (outputFile.exists()) {
            MediaPlayer().apply {
                setDataSource(mContext, Uri.fromFile(outputFile))
                setOnCompletionListener { mp -> mp.start() }
                setVolume(1F, 1F)
                prepare()
            }
        } else {
            // 录制失败则恢复原音效
            MediaPlayer.create(mContext, shush1).apply {
                setOnCompletionListener { mp -> mp.start() }
                setVolume(1F, 1F)
            }
        }
    }
    isRecording = !isRecording
}, modifier = Modifier.size(250.dp)) {
    Image(
        painter = painterResource(imageResource),
        contentDescription = "recording button"
    )
}

4. 修复MediaPlayer生命周期问题

当前代码在Composable中直接创建MediaPlayer会导致内存泄漏,建议用remember和DisposableEffect管理:

@Composable
fun generateButtons(modifier: Modifier = Modifier) {
    val mContext = LocalContext.current
    var mediaPlayer by remember { mutableStateOf<MediaPlayer?>(null) }
    var recorder: MediaRecorder? = null
    var isRecording by remember { mutableStateOf(false) }
    
    // 管理MediaPlayer生命周期
    DisposableEffect(Unit) {
        mediaPlayer = MediaPlayer.create(mContext, shush1).apply {
            setOnCompletionListener { mp -> mp.start() }
            setVolume(1F, 1F)
        }
        onDispose {
            mediaPlayer?.release()
            recorder?.release()
        }
    }
    
    // 剩余UI代码不变...
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.08 17:03:11