Android本地音频播放遇ExoPlaybackException: Source error问题求助
Android本地音频播放异常:FileNotFoundException但可读取元数据
开发Android音乐播放器时,通过MediaStore.query获取本地音频文件数据并封装为Song对象,使用生成的content://格式URI创建MediaItem尝试播放时触发FileNotFoundException,但同一个URI可以正常读取音乐标题、封面、艺术家等元数据。
Song数据类
data class Song( val id: Long, val title: String, val album: String, val artist: String, val duration: Long, val uri: Uri )
URI生成方式
通过音频ID拼接MediaStore的外部内容URI:
val contentUri = ContentUris.withAppendedId( Media.EXTERNAL_CONTENT_URI, id ) // URI示例: content://media/external/audio/media/259
异常堆栈信息
Playback error androidx.media3.exoplayer.ExoPlaybackException: Source error at androidx.media3.exoplayer.ExoPlayerImplInternal.handleIoException(ExoPlayerImplInternal.java:684) at androidx.media3.exoplayer.ExoPlayerImplInternal.handleMessage(ExoPlayerImplInternal.java:656) at android.os.Handler.dispatchMessage(Handler.java:102) at android.os.Looper.loopOnce(Looper.java:210) at android.os.Looper.loop(Looper.java:299) at android.os.HandlerThread.run(HandlerThread.java:67) Caused by: androidx.media3.datasource.FileDataSource$FileDataSourceException: java.io.FileNotFoundException: 937: open failed: ENOENT (No such file or directory) at androidx.media3.datasource.FileDataSource.openLocalFile(FileDataSource.java:205) at androidx.media3.datasource.FileDataSource.open(FileDataSource.java:116) at androidx.media3.datasource.DefaultDataSource.open(DefaultDataSource.java:272) at androidx.media3.datasource.StatsDataSource.open(StatsDataSource.java:86) at androidx.media3.exoplayer.source.ProgressiveMediaPeriod$ExtractingLoadable.load(ProgressiveMediaPeriod.java:1006) at androidx.media3.exoplayer.upstream.Loader$LoadTask.run(Loader.java:414) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1137) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:637) at java.lang.Thread.run(Thread.java:1012) Caused by: java.io.FileNotFoundException: 937: open failed: ENOENT (No such file or directory) at libcore.io.IoBridge.open(IoBridge.java:574) at java.io.RandomAccessFile.<init>(RandomAccessFile.java:289) at java.io.RandomAccessFile.<init>(RandomAccessFile.java:152) at androidx.media3.datasource.FileDataSource.openLocalFile(FileDataSource.java:186) at androidx.media3.datasource.FileDataSource.open(FileDataSource.java:116) at androidx.media3.datasource.DefaultDataSource.open(DefaultDataSource.java:272) at androidx.media3.datasource.StatsDataSource.open(StatsDataSource.java:86) at androidx.media3.exoplayer.source.ProgressiveMediaPeriod$ExtractingLoadable.load(ProgressiveMediaPeriod.java:1006) at androidx.media3.exoplayer.upstream.Loader$LoadTask.run(Loader.java:414) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1137) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:637) at java.lang.Thread.run(Thread.java:1012) Caused by: android.system.ErrnoException: open failed: ENOENT (No such file or directory) at libcore.io.Linux.open(Native Method) at libcore.io.ForwardingOs.open(ForwardingOs.java:563) at libcore.io.BlockGuardOs.open(BlockGuardOs.java:274) at libcore.io.ForwardingOs.open(ForwardingOs.java:563) at android.app.ActivityThread$AndroidOs.open(ActivityThread.java:8125) at libcore.io.IoBridge.open(IoBridge.java:560) at java.io.RandomAccessFile.<init>(RandomAccessFile.java:289) at java.io.RandomAccessFile.<init>(RandomAccessFile.java:152) at androidx.media3.datasource.FileDataSource.openLocalFile(FileDataSource.java:186) at androidx.media3.datasource.FileDataSource.open(FileDataSource.java:116) at androidx.media3.datasource.DefaultDataSource.open(DefaultDataSource.java:272) at androidx.media3.datasource.StatsDataSource.open(StatsDataSource.java:86) at androidx.media3.exoplayer.source.ProgressiveMediaPeriod$ExtractingLoadable.load(ProgressiveMediaPeriod.java:1006) at androidx.media3.exoplayer.upstream.Loader$LoadTask.run(Loader.java:414) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1137) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:637) at java.lang.Thread.run(Thread.java:1012)
已排查项
- 已在Manifest中添加
READ_EXTERNAL_STORAGE权限 - 参考其他应用实现,通过音频ID生成URI(与当前做法一致)
- 尝试使用
uri.toString()替代Uri对象传入MediaItem
解决方案
1. 配置支持Content URI的DataSourceFactory
ExoPlayer默认的FileDataSource无法处理content://格式的URI,需要显式添加ContentDataSource.Factory到数据源工厂中,确保能通过ContentResolver访问媒体文件:
val context: Context = ... val userAgent = Util.getUserAgent(context, "YourMusicPlayerApp") // 创建支持Content URI的数据源工厂 val dataSourceFactory = DefaultDataSource.Factory( context, ContentDataSource.Factory(context), DefaultHttpDataSource.Factory().setUserAgent(userAgent) ) // 创建媒体源并设置给播放器 val mediaItem = MediaItem.fromUri(song.uri) val mediaSource = ProgressiveMediaSource.Factory(dataSourceFactory) .createMediaSource(mediaItem) player.setMediaSource(mediaSource)
2. 适配Android版本的权限要求
- Android 13+(API 33):需要申请
READ_MEDIA_AUDIO权限替代READ_EXTERNAL_STORAGE,在Manifest中添加:
同时需要动态申请该权限。<uses-permission android:name="android.permission.READ_MEDIA_AUDIO" /> - Android 10-12(API 29-32):即使拥有
READ_EXTERNAL_STORAGE权限,Scoped Storage限制直接访问文件路径,必须通过content://URI和ContentResolver访问,确保ExoPlayer使用ContentDataSource处理URI。
3. 提前验证URI有效性
播放前可通过ContentResolver检查URI是否可访问:
context.contentResolver.openInputStream(song.uri)?.use { inputStream -> // URI有效,可继续播放流程 } ?: run { // URI无效,提示用户文件不存在或无法访问 }
内容的提问来源于stack exchange,提问作者Farbod Bijary
相关产品推荐
相关产品推荐

