使用MediaMetadataRetriever setDataSource读取外部存储MP3触发IllegalArgumentException
解决MediaMetadataRetriever.setDataSource触发IllegalArgumentException的问题
我帮你分析下问题出在哪,以及对应的解决办法:
你遇到的IllegalArgumentException主要是因为直接用绝对路径字符串解析Uri的方式错误,再加上Android不同版本的存储权限、访问规则限制,导致系统无法正确定位并访问你的MP3文件。
具体解决步骤:
1. 修正Uri的获取方式
不能直接用Uri.parse("/sdcard/song.mp3")来构建Uri,这种方式不符合Android的文件Uri规范。正确的做法是先创建File对象,再根据系统版本生成对应的Uri:
File songFile = new File(Environment.getExternalStorageDirectory(), "song.mp3");
- Android 7.0以下可以直接用
Uri.fromFile(songFile)生成Uri - Android 7.0及以上需要用
FileProvider生成安全Uri(避免FileUriExposedException)
2. 确保申请了存储权限
Android 6.0(API 23)及以上版本,读取外部存储需要动态申请READ_EXTERNAL_STORAGE权限,否则会被系统拒绝访问文件。
你可以把加载封面的逻辑封装成方法,先检查权限再执行:
private static final int REQUEST_STORAGE_PERMISSION = 100; private MediaMetadataRetriever metaRetriver; private ImageView album_art; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); getInit(); // 检查存储权限 if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, REQUEST_STORAGE_PERMISSION); } else { loadAlbumArt(); } } // 权限申请回调 @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); if (requestCode == REQUEST_STORAGE_PERMISSION) { if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { loadAlbumArt(); } else { Toast.makeText(this, "需要存储权限才能读取专辑封面", Toast.LENGTH_SHORT).show(); } } } // 加载专辑封面的核心方法 private void loadAlbumArt() { metaRetriver = new MediaMetadataRetriever(); try { File songFile = new File(Environment.getExternalStorageDirectory(), "song.mp3"); // 先检查文件是否存在 if (!songFile.exists()) { Log.i("lolol", "文件不存在,请检查路径"); return; } Uri songUri; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { // Android 7.0+使用FileProvider获取安全Uri songUri = FileProvider.getUriForFile(this, getPackageName() + ".fileprovider", songFile); } else { songUri = Uri.fromFile(songFile); } metaRetriver.setDataSource(this, songUri); byte[] art = metaRetriver.getEmbeddedPicture(); if (art != null) { Bitmap songImage = BitmapFactory.decodeByteArray(art, 0, art.length); album_art.setImageBitmap(songImage); } else { Log.i("lolol", "art is null"); } } catch (Exception e) { e.printStackTrace(); Log.i("lolol", "加载失败: " + e.getMessage()); } finally { // 使用后释放资源,避免内存泄漏 if (metaRetriver != null) { metaRetriver.release(); } } } public void getInit() { album_art = findViewById(R.id.album_art); }
3. Android 7.0+配置FileProvider
如果你的APP目标版本是Android 7.0及以上,需要在AndroidManifest.xml中配置FileProvider:
<application> <!-- 其他配置 --> <provider android:name="androidx.core.content.FileProvider" android:authorities="${applicationId}.fileprovider" 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 xmlns:android="http://schemas.android.com/apk/res/android"> <!-- 允许访问外部存储根目录 --> <external-path name="external_files" path="." /> </paths>
额外注意事项
- 尽量避免硬编码
/sdcard路径,用Environment.getExternalStorageDirectory()获取外部存储根目录更可靠 - 每次使用
MediaMetadataRetriever后一定要调用release()释放资源 - 提前检查文件是否存在,避免因文件缺失导致的异常
内容的提问来源于stack exchange,提问作者yosaga
相关产品推荐
相关产品推荐

