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
相关产品推荐
相关产品推荐

