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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.12 10:35:59