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

Android请求读写权限获PERMISSION_DENIED,无法读取Documents目录文件

问题分析与解决方案

你的核心问题是Android 10+(API30及以上)的Scoped Storage机制限制了传统文件权限的作用,导致你申请的READ/WRITE_EXTERNAL_STORAGE权限无法获得对公共目录的完全访问权,进而出现权限被拒、文件不可见的情况。以下是具体问题拆解和解决方法:

现有代码的问题

  1. 权限逻辑错误:判断权限时使用&&,意味着只有当两个权限都未授予时才申请,但实际上API30+中WRITE_EXTERNAL_STORAGE已被弱化,且系统会拒绝这类权限申请。
  2. 路径混乱:tryFile中同时访问Documents/MyData和/storage/emulated/0/MyData两个不同目录,导致逻辑混乱。
  3. 不符合Scoped Storage规范:API30+强制启用分区存储,传统File类无法直接访问公共目录下的非自建文件,且自建文件在应用卸载后会被系统删除。

推荐解决方案:使用Storage Access Framework(SAF)

这是Google官方推荐的方式,无需申请危险权限,通过用户主动选择目录授权访问,完全符合分区存储规范。

步骤1:启动SAF让用户选择目标目录

private static final int REQUEST_CODE_SELECT_DIR = 100;

// 在需要触发目录选择的位置调用(如onCreate或按钮点击)
private void openDirectoryPicker() {
    Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);
    // 默认打开Documents目录
    Uri initialUri = DocumentsContract.buildDocumentUri(
            "com.android.externalstorage.documents",
            "primary:Documents"
    );
    intent.putExtra(DocumentsContract.EXTRA_INITIAL_URI, initialUri);
    startActivityForResult(intent, REQUEST_CODE_SELECT_DIR);
}

步骤2:处理用户选择的目录并持久化权限

@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    if (requestCode == REQUEST_CODE_SELECT_DIR && resultCode == RESULT_OK) {
        if (data != null) {
            Uri treeUri = data.getData();
            // 持久化权限,避免下次启动重复选择
            getContentResolver().takePersistableUriPermission(
                    treeUri,
                    Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION
            );
            // 遍历目标目录下的文件
            scanMyDataDirectory(treeUri);
        }
    }
}

步骤3:遍历目录并读取文本文件

private void scanMyDataDirectory(Uri treeUri) {
    DocumentFile rootDir = DocumentFile.fromTreeUri(this, treeUri);
    if (rootDir == null) return;

    // 找到Documents下的MyData子目录
    DocumentFile myDataDir = rootDir.findFile("MyData");
    if (myDataDir == null || !myDataDir.isDirectory()) return;

    // 遍历目录下的所有txt文件
    DocumentFile[] files = myDataDir.listFiles();
    if (files == null) return;
    for (DocumentFile file : files) {
        if (file.isFile() && file.getName() != null && file.getName().endsWith(".txt")) {
            readTextFile(file.getUri());
        }
    }
}

private void readTextFile(Uri fileUri) {
    try (InputStream inputStream = getContentResolver().openInputStream(fileUri);
         BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream))) {
        StringBuilder content = new StringBuilder();
        String line;
        while ((line = reader.readLine()) != null) {
            content.append(line).append("\n");
        }
        // 在这里处理读取到的JSON内容
        Log.i("FILE_CONTENT", content.toString());
    } catch (IOException e) {
        e.printStackTrace();
    }
}

备选方案:申请MANAGE_EXTERNAL_STORAGE权限(仅适用于特定场景)

如果你的应用是文件管理器、备份工具等需要批量访问全存储的类型,可以申请该特殊权限,但需注意Google Play的审核限制:

  1. Manifest添加权限
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"
    android:minSdkVersion="30" />
  1. 申请权限代码
private static final int REQUEST_CODE_MANAGE_STORAGE = 200;

private void checkManageStoragePermission() {
    if (!Environment.isExternalStorageManager()) {
        Intent intent = new Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION);
        Uri uri = Uri.fromParts("package", getPackageName(), null);
        intent.setData(uri);
        startActivityForResult(intent, REQUEST_CODE_MANAGE_STORAGE);
    } else {
        // 权限已授予,可直接用File类访问公共目录
        tryFile();
    }
}

关键说明

  • Android 13(API33)已完全废弃READ/WRITE_EXTERNAL_STORAGE权限,非媒体文件必须用SAF或特殊权限访问。
  • SAF方式无需权限申请,用户主动授权,符合Google的隐私规范,不会被Play商店限制。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.17 03:17:38