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

Android Studio中RecyclerView显示空音频条目 仅部分设备出现该问题

问题原因
  • 核心原因是音频查询逻辑缺少必要过滤条件:查询MediaStore时没有筛选有效音乐文件,系统内置的提示音、短音效等非音乐类音频会被查询到,这类文件大多没有标题、无法正常播放,就是你看到的顶部仅显示图标的空列表项,因为不同设备内置的系统音频数量不同,所以仅在部分设备复现。
  • Cursor遍历逻辑错误:调用cursor.moveToFirst()确认有数据后,直接使用while(cursor.moveToNext())遍历,会丢失查询结果的第一条数据。
  • 权限适配不兼容:Android 13(API 33)及以上版本废弃了READ_EXTERNAL_STORAGE权限,需要申请READ_MEDIA_AUDIO权限,部分高版本设备使用旧权限查询MediaStore会返回无效的空数据。
  • 排序规则写法不规范:音频排序错误使用了MediaStore.Video.Media.TITLE,虽然多数设备上常量值相同不影响,但部分定制ROM可能返回异常值。
修复方案

1. 修复音频查询逻辑(修改Songs.java的getSongs方法)

@RequiresApi(api = Build.VERSION_CODES.Q)
public static List<Songs> getSongs(Context context) {
    List<Songs> songsList = new ArrayList<Songs>();
    ContentResolver contentResolver = context.getContentResolver();
    Uri externalContentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
    String[] projection = new String[]{
            MediaStore.Audio.Media._ID,
            MediaStore.Audio.Media.TITLE,
    };
    // 新增过滤条件:仅查询有效音乐文件、文件大小大于100KB,排除短音效和空文件
    String selection = MediaStore.Audio.Media.IS_MUSIC + " = ? AND " + MediaStore.Audio.Media.SIZE + " > ?";
    String[] selectionArgs = new String[]{"1", "100000"};
    // 修复排序规则为音频类的TITLE
    String sortOrder = MediaStore.Audio.Media.TITLE + " ASC";

    Cursor cursor = contentResolver.query(
            externalContentUri,
            projection,
            selection,
            selectionArgs,
            sortOrder
    );

    if(!cursor.moveToFirst()) {
        AlertDialog.Builder builder = new AlertDialog.Builder(context);
        builder.setMessage(R.string.dialog_message).setTitle(R.string.dialog_title);
        builder.setCancelable(false);
        builder.setPositiveButton("OK", (dialog, which) -> android.os.Process.killProcess(android.os.Process.myPid()));
        AlertDialog alert = builder.create();
        alert.show();
    } else {
        int idColumn = cursor.getColumnIndexOrThrow(MediaStore.Audio.Media._ID);
        int nameColumn = cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.TITLE);
        // 改用do-while遍历,避免丢失第一条数据
        do {
            long id = cursor.getLong(idColumn);
            String song_name = cursor.getString(nameColumn);
            // 额外过滤标题为空的无效数据
            if (song_name == null || song_name.trim().isEmpty()) {
                continue;
            }
            Uri contentUri = ContentUris.withAppendedId(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, id);
            songsList.add(new Songs(contentUri, song_name));
        } while(cursor.moveToNext());
    }
    // 用完关闭Cursor,避免内存泄漏
    if (cursor != null && !cursor.isClosed()) {
        cursor.close();
    }
    return songsList;
}

2. 适配高版本权限

首先修改AndroidManifest.xml的权限声明:

<!-- 兼容Android 12及以下版本 -->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="32" />
<!-- Android 13及以上版本音频权限 -->
<uses-permission android:name="android.permission.READ_MEDIA_AUDIO" />

然后修改MainActivity.java的动态权限申请逻辑,根据系统版本申请对应权限,同时提前初始化RecyclerView避免多次初始化:

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    // 提前初始化RecyclerView和Adapter
    RecyclerView recyclerView = findViewById(R.id.recyclerView);
    recyclerView.setLayoutManager(new LinearLayoutManager(this));
    songs = new ArrayList<>();
    SongsAdapter songsAdapter = new SongsAdapter(songs, this, this);
    recyclerView.setAdapter(songsAdapter);

    // 根据系统版本申请对应权限
    String requestPermission = Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU ?
            Manifest.permission.READ_MEDIA_AUDIO :
            Manifest.permission.READ_EXTERNAL_STORAGE;

    Dexter.withContext(this)
            .withPermission(requestPermission)
            .withListener(new PermissionListener() {
                @RequiresApi(api = Build.VERSION_CODES.Q)
                @Override
                public void onPermissionGranted(PermissionGrantedResponse permissionGrantedResponse) {
                    // 权限通过后更新数据集,通知Adapter刷新
                    List<Songs> newSongs = Songs.getSongs(getApplicationContext());
                    songs.clear();
                    songs.addAll(newSongs);
                    songsAdapter.notifyDataSetChanged();
                }

                @Override
                public void onPermissionDenied(PermissionDeniedResponse permissionDeniedResponse) {
                    android.os.Process.killProcess(android.os.Process.myPid());
                }

                @Override
                public void onPermissionRationaleShouldBeShown(PermissionRequest permissionRequest, PermissionToken permissionToken) {
                    permissionToken.continuePermissionRequest();
                }
            })
            .check();
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.06 17:45:03