Android平台MediaPlayer无法启动问题排查求助
解决Android媒体播放器Scoped Storage权限拒绝问题
这个EACCES (Permission denied)错误是因为**Android 10及以上版本引入的Scoped Storage(分区存储)**限制导致的——哪怕你申请了READ_EXTERNAL_STORAGE权限,直接通过文件路径访问外部存储的文件也会被系统拒绝。下面是具体的原因分析和解决方案:
问题根源
从Android 10(API 29)开始,系统默认启用分区存储,禁止应用直接通过绝对文件路径(比如/storage/emulated/0/Download/Naruto - Main Theme.mp3)访问外部存储文件。你当前通过MediaStore.Audio.Media.DATA获取的路径,在高版本系统上已经失去了直接访问的权限。
解决方案
我们需要改用Content URI(媒体库提供的统一资源标识)来访问音频文件,同时调整代码逻辑适配分区存储规范:
1. 修改MainActivity的歌曲加载逻辑
不再依赖DATA列获取文件路径,而是获取歌曲的Content URI,并更新Song类的字段存储URI字符串:
private void loadSongs() { Uri uri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI; String select = MediaStore.Audio.Media.IS_MUSIC + "!=0"; // 只查询必要字段,避免冗余 String[] projection = { MediaStore.Audio.Media._ID, MediaStore.Audio.Media.ARTIST, MediaStore.Audio.Media.DISPLAY_NAME }; Cursor cursor = getContentResolver().query(uri, projection, select, null, null); if(cursor!=null){ while (cursor.moveToNext()){ long songId = cursor.getLong(cursor.getColumnIndex(MediaStore.Audio.Media._ID)); String author = cursor.getString(cursor.getColumnIndex(MediaStore.Audio.Media.ARTIST)); String title = cursor.getString(cursor.getColumnIndex(MediaStore.Audio.Media.DISPLAY_NAME)); // 构建歌曲的Content URI Uri songUri = ContentUris.withAppendedId(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, songId); songs.add(new Song(title, author, songUri.toString())); } cursor.close(); // 记得关闭Cursor,避免内存泄漏 } recyclerView.setAdapter(new MusicAdapter(songs,this)); }
2. 调整MusicAdapter的播放逻辑
通过Content URI直接给MediaPlayer设置数据源,替代原来的文件路径方式:
private void startMusic(Integer pos) { // 先停止之前的播放,避免资源占用 if(mediaPlayer != null){ stopPlayer(); } mediaPlayer = new MediaPlayer(); try { Uri songUri = Uri.parse(songs.get(pos).song_Url); // 使用Content URI设置MediaPlayer数据源 mediaPlayer.setDataSource(context, songUri); mediaPlayer.prepare(); mediaPlayer.start(); } catch (IOException e) { e.printStackTrace(); Log.e("MusicAdapter", "播放失败: " + e.getMessage()); } }
3. 额外优化建议
- 权限提示优化:在权限被拒绝时给用户明确提示,提升体验:
@Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); if(requestCode == 111){ if(grantResults[0] == PackageManager.PERMISSION_GRANTED){ loadSongs(); } else { Toast.makeText(this, "需要存储权限才能读取本地歌曲", Toast.LENGTH_SHORT).show(); } } }
- 资源释放:在Activity销毁时调用
MusicAdapter的stopPlayer方法,释放MediaPlayer资源,避免内存泄漏。
内容的提问来源于stack exchange,提问作者Rishabh Malhotra
相关产品推荐
相关产品推荐

