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

为何申请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文件仍无法显示,请问问题出在哪里,该如何修改?


问题分析与解决方案

核心问题点

  1. 目录遍历逻辑错误:findPdf方法中仅遍历隐藏目录(singleFile.isDirectory() && singleFile.isHidden()),普通可见目录完全被跳过,根本无法扫描到大部分PDF文件。
  2. Android 10+分区存储限制:targetSdk为32,Android 10(API29)起引入分区存储机制,Environment.getExternalStorageDirectory()无法直接遍历整个外部存储,即使拥有READ_EXTERNAL_STORAGE权限也会受限。
  3. 空指针风险: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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.21 20:05:10