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

Android开发:Fragment读取Documents/PDFScanner目录PDF返回空数组如何解决

问题排查与修复方案

核心问题汇总

你当前代码存在权限配置缺失、查询逻辑错误、字段获取错误、逻辑流程错误四类问题,逐一对应修复即可:

  • 权限配置不符合不同Android版本的存储访问要求
  • MediaStore查询条件、投影字段配置错误
  • 废弃字段使用、路径获取逻辑完全错误
  • 流程逻辑存在空指针、数据漏读问题

分步修复方案

1. 权限配置(AndroidManifest.xml)

根据适配的API等级补充对应权限,运行时需主动向用户申请敏感权限:

<!-- 读写外部存储权限,Android 13及以上自动失效 -->
<uses-permission
    android:name="android.permission.READ_EXTERNAL_STORAGE"
    android:maxSdkVersion="32" />
<uses-permission
    android:name="android.permission.WRITE_EXTERNAL_STORAGE"
    android:maxSdkVersion="29" />
<!-- Android 13及以上如需读取非本应用生成的PDF,可申请以下权限或使用存储访问框架SAF -->
<uses-permission android:name="android.permission.READ_MEDIA_DOCUMENTS" />
<!-- 如需全文件访问,可申请MANAGE_EXTERNAL_STORAGE(上架Google Play需符合权限政策) -->

运行时需主动申请上述危险权限,拿到权限后再执行文件查询逻辑。

2. 代码错误点说明

原代码存在以下核心错误:

  • 投影字段只声明了_ID、DISPLAY_NAME,后续读取的BUCKET_DISPLAY_NAME、路径字段未加入投影,必然返回空值
  • 查询条件LIKE未加通配符%,只能匹配完全等于Documents/PDFScanner的路径,无法匹配目录下的文件路径
  • 已废弃的DATA字段在Android 10及以上无法正常返回文件路径
  • 先执行moveToFirst再执行moveToNext会漏掉第一条查询结果
  • MediaScannerConnection调用位置错误,此时file对象未初始化,会触发空指针

3. 修复后的代码示例

private void getPDFFilesFromDirecotry() {
    viewFilesArray.clear();
    Uri collection;
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
        collection = MediaStore.Files.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY);
    } else {
        collection = MediaStore.Files.getContentUri("external");
    }

    // 补充需要查询的所有字段
    String[] projection = {
            MediaStore.Files.FileColumns._ID,
            MediaStore.Files.FileColumns.DISPLAY_NAME,
            MediaStore.Files.FileColumns.SIZE,
            MediaStore.Files.FileColumns.DATE_MODIFIED,
            Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q ? MediaStore.Files.FileColumns.RELATIVE_PATH : MediaStore.Files.FileColumns.DATA
    };

    String selection;
    String[] selectionArgs;
    String pdfMimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension("pdf");
    
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
        // Android 10及以上用RELATIVE_PATH匹配目录
        selection = MediaStore.Files.FileColumns.RELATIVE_PATH + " LIKE ? AND " + MediaStore.Files.FileColumns.MIME_TYPE + " = ?";
        selectionArgs = new String[]{"%" + Environment.DIRECTORY_DOCUMENTS + "/PDFScanner/%", pdfMimeType};
    } else {
        // Android 10以下用DATA匹配路径
        selection = MediaStore.Files.FileColumns.DATA + " LIKE ? AND " + MediaStore.Files.FileColumns.MIME_TYPE + " = ?";
        selectionArgs = new String[]{"%" + Environment.DIRECTORY_DOCUMENTS + "/PDFScanner/%.pdf", pdfMimeType};
    }

    Cursor mediaCursor = applicationContext.getContentResolver().query(
            collection,
            projection,
            selection,
            selectionArgs,
            MediaStore.Files.FileColumns.DATE_MODIFIED + " DESC"
    );

    if (mediaCursor == null) {
        Log.e("TAG", "Cursor is null");
        ivNoData.setVisibility(View.VISIBLE);
        return;
    }

    Log.e("TAG", "Query result count: " + mediaCursor.getCount());
    if (mediaCursor.getCount() <= 0) {
        Log.e("TAG", "No data found");
        ivNoData.setVisibility(View.VISIBLE);
        mediaCursor.close();
        return;
    }

    @SuppressLint("SimpleDateFormat")
    SimpleDateFormat dateFormat = new SimpleDateFormat("dd-MMM-yy hh.mm a");
    int pathColumnIndex = Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q ? 
            mediaCursor.getColumnIndexOrThrow(MediaStore.Files.FileColumns.RELATIVE_PATH) :
            mediaCursor.getColumnIndexOrThrow(MediaStore.Files.FileColumns.DATA);
    int nameColumnIndex = mediaCursor.getColumnIndexOrThrow(MediaStore.Files.FileColumns.DISPLAY_NAME);
    int sizeColumnIndex = mediaCursor.getColumnIndexOrThrow(MediaStore.Files.FileColumns.SIZE);
    int dateColumnIndex = mediaCursor.getColumnIndexOrThrow(MediaStore.Files.FileColumns.DATE_MODIFIED);

    while (mediaCursor.moveToNext()) {
        String pathOrRelative = mediaCursor.getString(pathColumnIndex);
        String fileName = mediaCursor.getString(nameColumnIndex);
        long size = mediaCursor.getLong(sizeColumnIndex);
        long modifiedTime = mediaCursor.getLong(dateColumnIndex) * 1000; // 转换为毫秒

        String fullPath;
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
            fullPath = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS) + "/PDFScanner/" + fileName;
        } else {
            fullPath = pathOrRelative;
        }

        // 处理文件名
        String showName = fileName;
        int dotIndex = fileName.lastIndexOf(".");
        if (dotIndex > 0) {
            showName = fileName.substring(0, dotIndex);
        }

        // 处理日期
        String dateStr = dateFormat.format(new Date(modifiedTime));
        // 处理大小
        String sizeStr = formatSize(size);

        viewFilesArray.add(new ViewFilesModel(fullPath, showName, sizeStr, dateStr));
    }

    mediaCursor.close();
    ivNoData.setVisibility(viewFilesArray.isEmpty() ? View.VISIBLE : View.GONE);
    filesAdapter.notifyDataSetChanged();
    Log.e("TAG", "Loaded file count: " + viewFilesArray.size());
}

额外注意事项

  • 如果PDF文件是本应用生成的,生成后要主动触发MediaScanner扫描,确保文件被MediaStore收录,否则查询不到
  • Android 11及以上如果需要访问其他应用生成的PDF文件,建议使用存储访问框架(SAF)让用户主动授权对应目录,避免申请全文件访问权限影响上架
  • 不要直接通过File API访问公共目录下的文件,Android 10及以上会触发权限错误,优先通过ContentResolver访问MediaStore返回的Uri

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.30 07:54:04