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
相关产品推荐
相关产品推荐

