Android请求读写权限获PERMISSION_DENIED,无法读取Documents目录文件
问题分析与解决方案
你的核心问题是Android 10+(API30及以上)的Scoped Storage机制限制了传统文件权限的作用,导致你申请的READ/WRITE_EXTERNAL_STORAGE权限无法获得对公共目录的完全访问权,进而出现权限被拒、文件不可见的情况。以下是具体问题拆解和解决方法:
现有代码的问题
- 权限逻辑错误:判断权限时使用
&&,意味着只有当两个权限都未授予时才申请,但实际上API30+中WRITE_EXTERNAL_STORAGE已被弱化,且系统会拒绝这类权限申请。 - 路径混乱:
tryFile中同时访问Documents/MyData和/storage/emulated/0/MyData两个不同目录,导致逻辑混乱。 - 不符合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的审核限制:
- Manifest添加权限
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" android:minSdkVersion="30" />
- 申请权限代码
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
相关产品推荐
相关产品推荐

