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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.16 10:08:19