为何申请READ_EXTERNAL_STORAGE权限后Android App仍无法显示PDF?
问题描述
我正在开发一款支持用户读取PDF文件的Android App,已在清单文件中添加权限配置,代码逻辑看似正常,但当授予READ_EXTERNAL_STORAGE运行时权限后,App中仍无法显示可供选择的PDF文件。
目标适配SDK版本为21至最新版本,当前gradle中compileSdk、minSdk、targetSdk均为32。
MainActivity.java 代码
public class MainActivity extends AppCompatActivity { private PdfAdapter pdfAdapter; private List<File> pdfList; private RecyclerView recyclerView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); runtimePermission(); } private void runtimePermission() { Dexter.withContext(MainActivity.this).withPermission(Manifest.permission.READ_EXTERNAL_STORAGE) .withListener(new PermissionListener() { @Override public void onPermissionGranted(PermissionGrantedResponse permissionGrantedResponse) { displayPdf(); } @Override public void onPermissionDenied(PermissionDeniedResponse permissionDeniedResponse) { Toast.makeText(MainActivity.this, "Permission is Required", Toast.LENGTH_SHORT).show(); } @Override public void onPermissionRationaleShouldBeShown(PermissionRequest permissionRequest, PermissionToken permissionToken) { permissionToken.continuePermissionRequest(); } }).check(); } public ArrayList<File> findPdf (File file) { ArrayList<File> arrayList = new ArrayList<>(); File[] files = file.listFiles(); for (File singleFile: files) { if (singleFile.isDirectory() && singleFile.isHidden()) { arrayList.addAll(findPdf(singleFile)); } else { if (singleFile.getName().endsWith(".pdf")){ arrayList.add(singleFile); } } } return arrayList; } private void displayPdf() { recyclerView = findViewById(R.id.recycler_view); recyclerView.setHasFixedSize(true); recyclerView.setLayoutManager(new GridLayoutManager(this, 3)); pdfList = new ArrayList<>(); pdfList.addAll(findPdf(Environment.getExternalStorageDirectory())); pdfAdapter = new PdfAdapter(this, pdfList); recyclerView.setAdapter(pdfAdapter); } }
我已配置PdfAdapter.java和PdfViewHolder.java,但PDF文件仍无法显示,请问问题出在哪里,该如何修改?
问题分析与解决方案
核心问题点
- 目录遍历逻辑错误:
findPdf方法中仅遍历隐藏目录(singleFile.isDirectory() && singleFile.isHidden()),普通可见目录完全被跳过,根本无法扫描到大部分PDF文件。 - Android 10+分区存储限制:targetSdk为32,Android 10(API29)起引入分区存储机制,
Environment.getExternalStorageDirectory()无法直接遍历整个外部存储,即使拥有READ_EXTERNAL_STORAGE权限也会受限。 - 空指针风险:
file.listFiles()可能返回null(比如无权限访问目录、目录不存在),直接遍历会触发空指针异常导致流程中断。
具体修改方案
1. 修复目录遍历逻辑
修改findPdf方法,去掉隐藏目录的限制,同时增加空指针判断:
public ArrayList<File> findPdf(File file) { ArrayList<File> arrayList = new ArrayList<>(); File[] files = file.listFiles(); // 避免空指针异常 if (files == null) { return arrayList; } for (File singleFile : files) { if (singleFile.isDirectory()) { // 递归遍历所有目录(无论是否隐藏) arrayList.addAll(findPdf(singleFile)); } else { // 大小写不敏感匹配PDF后缀,避免漏掉大写后缀的文件 if (singleFile.getName().toLowerCase().endsWith(".pdf")) { arrayList.add(singleFile); } } } return arrayList; }
2. 适配Android 10+分区存储(推荐方案)
Android 10+官方推荐使用MediaStore API查询媒体文件,替换原有的文件遍历逻辑:
private void displayPdf() { recyclerView = findViewById(R.id.recycler_view); recyclerView.setHasFixedSize(true); recyclerView.setLayoutManager(new GridLayoutManager(this, 3)); pdfList = new ArrayList<>(); // 使用MediaStore加载PDF文件 loadPdfFromMediaStore(); pdfAdapter = new PdfAdapter(this, pdfList); recyclerView.setAdapter(pdfAdapter); } private void loadPdfFromMediaStore() { // 查询所需字段 String[] projection = { MediaStore.Files.FileColumns._ID, MediaStore.Files.FileColumns.DISPLAY_NAME, MediaStore.Files.FileColumns.DATA }; // 筛选PDF文件(MIME类型为application/pdf) String selection = MediaStore.Files.FileColumns.MIME_TYPE + " = ?"; String[] selectionArgs = {"application/pdf"}; Cursor cursor = getContentResolver().query( MediaStore.Files.getContentUri("external"), projection, selection, selectionArgs, null ); if (cursor != null) { while (cursor.moveToNext()) { // Android 10+ 优先使用ContentUri访问文件,DATA字段可能为空 String filePath = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Files.FileColumns.DATA)); if (filePath != null) { pdfList.add(new File(filePath)); } } cursor.close(); } }
3. 权限适配补充
- 对于Android 13(API33),
READ_EXTERNAL_STORAGE已被废弃,若需兼容,可在清单文件中添加:
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="32" /> <uses-permission android:name="android.permission.READ_MEDIA_IMAGES" /> <uses-permission android:name="android.permission.READ_MEDIA_VIDEO" /> <uses-permission android:name="android.permission.READ_MEDIA_AUDIO" />
- 若需要访问所有文件,可申请
MANAGE_EXTERNAL_STORAGE权限,但该权限需通过Google Play审核,仅适用于特定场景(如文件管理器类App)。
内容的提问来源于stack exchange,提问作者Marfog509
相关产品推荐
相关产品推荐

