Android 11分区存储无需MANAGE_EXTERNAL_STORAGE权限获取全部PDF文件
Android11分区存储下无需MANAGE_EXTERNAL_STORAGE获取PDF文件解决方案
问题核心原因
你遇到的问题是Android11分区存储权限限制和旧代码API使用不当共同导致的:
- Android11+没有
MANAGE_EXTERNAL_STORAGE权限时,MediaStore.Files表默认仅返回当前应用创建的公共存储文件,其他应用生成的PDF无法通过该接口查询MediaStore.Files.FileColumns.DATA列在Android11后已废弃,第三方应用无权限直接访问该路径,new File(path).exists()永远返回false- 部分PDF文件的MIME_TYPE未被系统正确识别,仅用MIME_TYPE筛选会漏检文件
前置配置
- AndroidManifest.xml权限配置
<!-- Android 6~12需要读取存储权限,Android13+该权限仅能访问媒体文件,非媒体PDF需用SAF方案 --> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="32" /> <application ... <!-- 仅Android10生效,Android11自动忽略 --> android:requestLegacyExternalStorage="true" ...>
- 先动态申请
READ_EXTERNAL_STORAGE权限(Android6~12),权限未授予时查询MediaStore会返回空游标
方案1:仅查询当前应用创建的PDF(无需用户手动授权目录)
修改后的代码直接返回可直接访问的文件Uri,不要使用本地路径:
protected ArrayList<Uri> getPdfList() { ArrayList<Uri> pdfList = new ArrayList<>(); Uri collection; final String[] projection = new String[]{ MediaStore.Files.FileColumns._ID, MediaStore.Files.FileColumns.DISPLAY_NAME, MediaStore.Files.FileColumns.DATE_ADDED, MediaStore.Files.FileColumns.MIME_TYPE, }; final String sortOrder = MediaStore.Files.FileColumns.DATE_ADDED + " DESC"; // 同时筛选MIME_TYPE和文件名后缀,避免漏检 final String selection = MediaStore.Files.FileColumns.MIME_TYPE + " = ? OR " + MediaStore.Files.FileColumns.DISPLAY_NAME + " LIKE '%.pdf'"; final String mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension("pdf"); final String[] selectionArgs = new String[]{mimeType}; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { collection = MediaStore.Files.getContentUri(MediaStore.VOLUME_EXTERNAL); } else { collection = MediaStore.Files.getContentUri("external"); } try (Cursor cursor = getContentResolver().query(collection, projection, selection, selectionArgs, sortOrder)) { if (cursor != null && cursor.moveToFirst()) { int columnId = cursor.getColumnIndexOrThrow(MediaStore.Files.FileColumns._ID); do { // 拼接可直接访问的Content Uri Uri pdfUri = ContentUris.withAppendedId(collection, cursor.getLong(columnId)); pdfList.add(pdfUri); } while (cursor.moveToNext()); } } catch (SecurityException e) { e.printStackTrace(); } return pdfList; }
拿到Uri后可通过getContentResolver().openInputStream(uri)读取文件内容。
方案2:获取全量PDF(无需MANAGE_EXTERNAL_STORAGE权限)
如果需要拿到手机里所有PDF文件,用存储访问框架(SAF)引导用户授权存储目录即可,无需特殊权限:
- 唤起SAF让用户选择存储根目录授权
private static final int REQUEST_SAF_CODE = 1001; private void requestStorageAccess() { Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE); intent.addFlags( Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION // 持久化授权,APP重启后不需要重新申请 | Intent.FLAG_GRANT_PREFIX_URI_PERMISSION ); startActivityForResult(intent, REQUEST_SAF_CODE); }
- 授权成功后遍历目录下所有PDF
@Override protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) { super.onActivityResult(requestCode, resultCode, data); if (requestCode == REQUEST_SAF_CODE && resultCode == RESULT_OK && data != null) { Uri treeUri = data.getData(); // 保存持久化授权 getContentResolver().takePersistableUriPermission(treeUri, Intent.FLAG_GRANT_READ_URI_PERMISSION); ArrayList<Uri> allPdfList = traversePdfFromTree(treeUri); } } private ArrayList<Uri> traversePdfFromTree(Uri treeUri) { ArrayList<Uri> pdfList = new ArrayList<>(); DocumentFile root = DocumentFile.fromTreeUri(this, treeUri); if (root == null || !root.exists()) return pdfList; Queue<DocumentFile> dirQueue = new LinkedList<>(); dirQueue.add(root); while (!dirQueue.isEmpty()) { DocumentFile current = dirQueue.poll(); if (current.isDirectory()) { DocumentFile[] children = current.listFiles(); for (DocumentFile child : children) { dirQueue.add(child); } } else { String fileName = current.getName(); if (fileName != null && fileName.toLowerCase().endsWith(".pdf")) { pdfList.add(current.getUri()); } } } return pdfList; }
内容的提问来源于stack exchange,提问作者ronak patel
相关产品推荐
相关产品推荐

