MediaStore.Audio.Media.DATA返回无效路径致setDataSource异常求助
问题分析与解决方案
我来帮你梳理下问题根源和解决办法:
为什么会出现这个异常?
- MediaStore数据不同步:当音频文件被第三方应用删除、SD卡移除或用户手动删除后,系统的MediaStore数据库不会立刻同步更新,导致你从Cursor中拿到的
DATA字段指向了一个已经不存在的文件。 - DATA字段已废弃:从Android Q(API 29)开始,
MediaStore.Audio.Media.DATA就被标记为废弃,直接依赖这个字段获取路径的方式在高版本系统中会越来越不可靠。 - 存储权限限制:即使你申请了存储权限,在Android 10+的分区存储机制下,直接访问文件路径也可能遇到隐性权限问题,触发
setDataSource的参数异常。
具体解决方案
1. 紧急修复:先检查文件是否存在
在调用setDataSource前,先判断文件真实存在性,避免抛出异常:
String thisPath = cursor.getString(pathColumn); File audioFile = new File(thisPath); Bitmap bitmap = null; if (audioFile.exists() && audioFile.isFile()) { MediaMetadataRetriever mmr = new MediaMetadataRetriever(); try { mmr.setDataSource(thisPath); byte[] data = mmr.getEmbeddedPicture(); if (data != null) { bitmap = BitmapFactory.decodeByteArray(data, 0, data.length); } } catch (IllegalArgumentException e) { e.printStackTrace(); bitmap = null; } finally { mmr.release(); // 必须释放资源,避免内存泄漏 } } else { // 文件不存在,跳过当前条目 bitmap = null; } songList.add(new Song(thisId, thisTitle, thisArtist, thisAlbum, bitmap));
2. 长期适配:改用Content Uri访问媒体文件
这是Android官方推荐的方式,既避免路径失效问题,又能适配高版本分区存储:
ContentResolver contentResolver = getContentResolver(); Uri uri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI; Cursor cursor = contentResolver.query(uri, null, null, null, null); if (cursor != null && cursor.moveToFirst()) { // 获取必要字段的索引 int idColumn = cursor.getColumnIndex(MediaStore.Audio.Media._ID); int titleColumn = cursor.getColumnIndex(MediaStore.Audio.Media.TITLE); int artistColumn = cursor.getColumnIndex(MediaStore.Audio.Media.ARTIST); int albumColumn = cursor.getColumnIndex(MediaStore.Audio.Media.ALBUM); do { long audioId = cursor.getLong(idColumn); // 通过音频ID构建Content Uri Uri audioUri = ContentUris.withAppendedId(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, audioId); String thisTitle = cursor.getString(titleColumn); String thisArtist = cursor.getString(artistColumn); String thisAlbum = cursor.getString(albumColumn); Bitmap bitmap = null; MediaMetadataRetriever mmr = new MediaMetadataRetriever(); try { // 使用Content Uri和ContentResolver设置数据源 mmr.setDataSource(this, audioUri); byte[] data = mmr.getEmbeddedPicture(); if (data != null) { bitmap = BitmapFactory.decodeByteArray(data, 0, data.length); } } catch (IllegalArgumentException e) { // 处理无效媒体条目 e.printStackTrace(); bitmap = null; } finally { mmr.release(); } songList.add(new Song(audioId, thisTitle, thisArtist, thisAlbum, bitmap)); } while (cursor.moveToNext()); cursor.close(); // 关闭Cursor释放资源 }
3. 优化:主动更新MediaStore(可选)
如果检测到文件不存在,可以通知系统更新MediaStore,避免后续查询再拿到无效条目:
// 删除MediaStore中无效的记录 contentResolver.delete(audioUri, null, null); // 或者发送广播让系统重新扫描媒体文件 Intent scanIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE); scanIntent.setData(Uri.fromFile(audioFile)); sendBroadcast(scanIntent);
额外注意事项
- 务必调用
mmr.release()和cursor.close(),避免内存泄漏。 - 在Android 13+,需要申请
READ_MEDIA_AUDIO权限替代旧的READ_EXTERNAL_STORAGE。
内容的提问来源于stack exchange,提问作者b0ttas
相关产品推荐
相关产品推荐

