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

API 29+下MediaStore音频数据获取失效,求替代实现方案

API 29+ 离线音乐播放器适配方案

核心问题

API 29(Android 10)开始引入分区存储(Scoped Storage),MediaStore.Audio.Media.DATA字段被废弃,无法直接获取音乐文件的绝对路径,这是原有代码失效的根本原因。官方推荐通过Content URI访问媒体资源,而非直接操作文件路径。

适配步骤与代码修改

1. 权限适配

  • API 33及以上:申请READ_MEDIA_AUDIO权限
  • API 29~32:申请READ_EXTERNAL_STORAGE权限
    在AndroidManifest.xml中添加权限:
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="32" />
<uses-permission android:name="android.permission.READ_MEDIA_AUDIO" android:minSdkVersion="33" />

运行时需动态申请对应权限,确保权限通过后再执行媒体查询。

2. 修改媒体查询逻辑

不再获取DATA字段,改用_ID构建Content URI作为音乐资源的唯一标识,其他字段(标题、专辑、艺术家等)可正常获取。修改后的loadData方法如下:

public void loadData() {
    ContentResolver contentResolver = requireContext().getContentResolver();

    Uri uri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
    String selection = MediaStore.Audio.Media.IS_MUSIC + "!= 0";
    String sortOrder = MediaStore.MediaColumns.TITLE + " ASC";
    // 明确指定需要查询的列,减少冗余数据
    String[] projection = {
            MediaStore.Audio.Media._ID,
            MediaStore.MediaColumns.TITLE,
            MediaStore.Audio.Media.ALBUM,
            MediaStore.Audio.Media.ARTIST,
            MediaStore.Audio.Media.DURATION
    };

    Cursor cursor = contentResolver.query(uri, projection, selection, null, sortOrder);

    if (cursor != null && cursor.getCount() > 0) {
        model = new ArrayList<>();
        while (cursor.moveToNext()) {
            // 通过ID构建Content URI
            long id = cursor.getLong(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media._ID));
            Uri contentUri = ContentUris.withAppendedId(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, id);
            
            String title = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.MediaColumns.TITLE));
            String album = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.ALBUM));
            String artist = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.ARTIST));
            long duration = cursor.getLong(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.DURATION));
            int calculatedDuration = (int) (duration / 60000); // 转换为分钟(注意单位是毫秒)

            // 用Content URI替代原有的DATA路径存入模型
            model.add(new MusicModel(contentUri.toString(), title, album, artist));
            
            Log.e("test",
                    "\n contentUri = " + contentUri +
                    "\n title = " + title +
                    "\n album = " + album +
                    "\n artist = " + artist +
                    "\n duration = " + calculatedDuration);
        }
        cursor.close();
    }

    ArtistAdapter adapter = new ArtistAdapter(requireContext(), model);
    binding.rvArtist.setAdapter(adapter);
    binding.rvArtist.setHasFixedSize(true);
}

3. 播放逻辑适配

播放音乐时,不能再使用文件路径,需通过ContentResolver获取文件描述符,示例(以MediaPlayer为例):

// 从MusicModel中取出Content URI字符串
Uri audioUri = Uri.parse(musicModel.getContentUri());
MediaPlayer mediaPlayer = new MediaPlayer();

try {
    ParcelFileDescriptor pfd = getContentResolver().openFileDescriptor(audioUri, "r");
    if (pfd != null) {
        mediaPlayer.setDataSource(pfd.getFileDescriptor());
        pfd.close();
        mediaPlayer.prepare();
        mediaPlayer.start();
    }
} catch (IOException e) {
    e.printStackTrace();
}

关键注意事项

  • 不要尝试通过反射或非官方方式获取文件路径,分区存储下这类方法会失效或触发安全问题。
  • 若需缓存封面等资源,可通过Content URI读取文件流后保存到应用私有目录,不要直接操作外部存储文件。
  • 测试时需在API29+的设备或模拟器上验证,确保权限和逻辑正常。

内容的提问来源于stack exchange,提问作者efesydss

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.18 19:20:47