Android 10中MediaMetadataRetriever抛出异常该如何解决?
问题根因
Android 10(API 级别 29)引入分区存储机制后,即便在Manifest中配置了android:requestLegacyExternalStorage="true",MediaMetadataRetriever.setDataSource(String path)方法对共享存储内的非应用自有媒体文件的访问依然会被系统限制,直接传入文件路径会触发IllegalArgumentException异常。另外你原代码未主动释放MediaMetadataRetriever资源,也会引发内存泄漏、偶发访问冲突等问题。
解决方案
方案1:使用Content Uri作为数据源(推荐)
从MediaStore查询音频文件时,不要只存文件路径,同时保存MediaStore返回的content://格式的Uri,使用带Context参数的setDataSource方法加载,适配分区存储规则。
修改后代码如下:
private Bitmap getAlbumImage(Context context, Uri audioUri) { MediaMetadataRetriever mmr = new MediaMetadataRetriever(); try { mmr.setDataSource(context, audioUri); byte[] data = mmr.getEmbeddedPicture(); if (data != null) { return BitmapFactory.decodeByteArray(data, 0, data.length); } } catch (IOException | IllegalArgumentException e) { // 异常捕获避免崩溃,损坏的音频文件直接返回空即可 e.printStackTrace(); } finally { // 必须主动释放资源 try { mmr.release(); } catch (IOException e) { e.printStackTrace(); } } return null; }
方案2:通过文件描述符加载(仅适用于仍需使用文件路径的场景)
如果业务逻辑必须使用文件路径,可以先打开文件的文件描述符再传给MediaMetadataRetriever:
private Bitmap getAlbumImage(String path) { MediaMetadataRetriever mmr = new MediaMetadataRetriever(); FileInputStream fis = null; try { fis = new FileInputStream(path); mmr.setDataSource(fis.getFD()); byte[] data = mmr.getEmbeddedPicture(); if (data != null) { return BitmapFactory.decodeByteArray(data, 0, data.length); } } catch (IOException | IllegalArgumentException e) { e.printStackTrace(); } finally { try { mmr.release(); if (fis != null) { fis.close(); } } catch (IOException e) { e.printStackTrace(); } } return null; }
优化建议
你可以直接从MediaStore查询音频对应的专辑封面Uri,不需要自行解析媒体文件,稳定性和性能更高:
- 查询音频时同时获取
MediaStore.Audio.Media.ALBUM_ID字段 - 拼接专辑封面Uri:
Uri albumArtUri = ContentUris.withAppendedId(Uri.parse("content://media/external/audio/albumart"), albumId); - 直接加载该Uri即可得到专辑封面
注意事项
- 必须提前申请对应存储权限:Android 10及以下动态申请
READ_EXTERNAL_STORAGE权限,Android 13及以上动态申请READ_MEDIA_AUDIO权限,权限未通过时不要执行媒体文件访问操作。
内容的提问来源于stack exchange,提问作者Spyware
相关产品推荐
相关产品推荐

