Android Kotlin:MediaPlayer播放FLAC临时文件首次运行崩溃问题
解决方案:FLAC文件播放崩溃修复及多文件播放、调速实现建议
一、修复首次运行崩溃问题
1. 补全FileProvider配置
FileProvider.getUriForFile会因未配置权限直接崩溃,必须在AndroidManifest.xml中注册Provider:
<application ...> <provider android:name="androidx.core.content.FileProvider" android:authorities="${applicationId}.provider" android:exported="false" android:grantUriPermissions="true"> <meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/file_paths" /> </provider> </application>
在res/xml目录下创建file_paths.xml,开放缓存目录访问权限:
<?xml version="1.0" encoding="utf-8"?> <paths> <cache-path name="cache_files" path="." /> </paths>
2. 完善文件复制逻辑(添加异常处理)
原代码未捕获IO异常,崩溃时无有效日志,需包裹异常处理并校验资源存在性:
try { val resId = resources.getIdentifier("zero", "raw", packageName) if (resId == 0) { // 资源不存在,提示用户或终止流程 return } resources.openRawResource(resId).use { input -> File(cacheDir, "output.flac").apply { // 删除旧文件避免残留损坏数据 delete() }.outputStream().use { output -> input.copyTo(output) } } } catch (e: IOException) { e.printStackTrace() // 处理文件复制失败场景 }
3. 优化MediaPlayer使用方式
prepare()是同步方法,主线程调用易引发ANR或崩溃,改用异步准备并添加错误监听:
val mp = MediaPlayer().apply { setOnPreparedListener { start() } setOnErrorListener { _, _, _ -> // 处理播放错误,返回true表示已处理 true } } try { val outputFile = File(cacheDir, "output.flac") val uri = FileProvider.getUriForFile(this, "$packageName.provider", outputFile) mp.setDataSource(this, uri) mp.prepareAsync() // 异步准备,避免阻塞主线程 } catch (e: IOException) { e.printStackTrace() mp.release() // 异常时释放资源 }
注意在Activity销毁时调用mp.release(),避免内存泄漏。
二、多文件合并播放与调速方案
1. 优先使用ExoPlayer替代MediaPlayer
MediaPlayer对FLAC的变速支持有限,多文件合并需手动处理流,ExoPlayer更适配需求:
- 原生支持FLAC格式
- 无缝实现多文件连续播放
- 支持0.5x-2x(甚至更高)的变速,且保持音调稳定
- 无需复制文件到缓存,可直接播放raw资源
示例代码(ExoPlayer多文件播放+调速)
先添加依赖(build.gradle):
implementation "com.google.android.exoplayer:exoplayer-core:2.19.1" implementation "com.google.android.exoplayer:exoplayer-flac:2.19.1" // FLAC格式支持
实现播放逻辑:
// 初始化ExoPlayer val player = ExoPlayer.Builder(this).build() // 根据用户输入构建播放列表(示例为zero、one、two) val mediaItems = listOf("zero", "one", "two").map { fileName -> val resId = resources.getIdentifier(fileName, "raw", packageName) val uri = RawResourceDataSource.buildRawResourceUri(resId) MediaItem.fromUri(uri) } // 设置播放列表并准备 player.setMediaItems(mediaItems) player.prepare() // 调速:正常(1.0f)、快速(1.25f)、更快(1.5f)、最快(2.0f) player.setPlaybackSpeed(1.25f) // 开始播放 player.play()
2. 若坚持使用MediaPlayer(不推荐)
需通过SequenceInputStream合并多个raw流到临时文件,变速依赖API23+的PlaybackParams:
// 合并多个raw文件到临时文件 val fileNames = listOf("zero", "one", "two") val tempFile = File(cacheDir, "merged.flac") tempFile.outputStream().use { output -> val inputStreams = fileNames.map { fileName -> resources.openRawResource(resources.getIdentifier(fileName, "raw", packageName)) } SequenceInputStream(Enumeration(inputStreams)).use { sequenceInput -> sequenceInput.copyTo(output) } } // 变速设置(API23+) val params = PlaybackParams().apply { speed = 1.5f // 更快速度 } mp.setPlaybackParams(params)
关键注意事项
- 不要在主线程执行文件复制、MediaPlayer同步准备等耗时操作,避免ANR
- 始终捕获IO异常并处理播放器错误回调
- ExoPlayer需在Activity销毁时调用
player.release()释放资源 - raw资源文件名避免使用大写字母或特殊字符,防止
getIdentifier返回0
内容的提问来源于stack exchange,提问作者lxuser
相关产品推荐
相关产品推荐

