Android 13文件夹读写权限申请及访问功能实现求助
正确实现Android指定文件夹读写(非媒体文件)
核心误区纠正
SAF(存储访问框架)完全支持非媒体文件(包括.obj、.atlas、.pack、.pfx这类格式),ACTION_OPEN_DOCUMENT_TREE正是用来获取整个文件夹访问权限的正确方式,之前的代码无效大概率是没处理持久化权限或文件操作逻辑错误。
步骤1:获取文件夹的持久化访问权限
使用ACTION_OPEN_DOCUMENT_TREE启动文件夹选择器,用户选择后需申请持久化权限,确保重启应用后仍能访问该文件夹:
// 启动文件夹选择器 Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE); intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION); intent.addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION); // 可选:指定初始打开的路径(比如下载文件夹) Uri initialUri = MediaStore.Downloads.EXTERNAL_CONTENT_URI; intent.putExtra(DocumentsContract.EXTRA_INITIAL_URI, initialUri); // 使用ActivityResultLauncher处理结果(替代旧的onActivityResult) ActivityResultLauncher<Intent> folderPickerLauncher = registerForActivityResult( new ActivityResultContracts.StartActivityForResult(), result -> { if (result.getResultCode() == Activity.RESULT_OK && result.getData() != null) { Uri treeUri = result.getData().getData(); if (treeUri != null) { // 申请持久化权限 getContentResolver().takePersistableUriPermission( treeUri, Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION ); // 保存treeUri到SharedPreferences,后续使用 SharedPreferences sp = getSharedPreferences("AppPrefs", MODE_PRIVATE); sp.edit().putString("selected_folder_uri", treeUri.toString()).apply(); } } } ); // 触发选择器 folderPickerLauncher.launch(intent);
步骤2:通过SAF读写非媒体文件
拿到持久化的treeUri后,用DocumentFile操作文件夹内的文件:
遍历文件夹内的目标文件
String savedUriStr = sp.getString("selected_folder_uri", null); if (savedUriStr != null) { Uri treeUri = Uri.parse(savedUriStr); DocumentFile rootFolder = DocumentFile.fromTreeUri(this, treeUri); if (rootFolder != null && rootFolder.isDirectory()) { // 筛选出.obj文件 for (DocumentFile file : rootFolder.listFiles()) { if (file.getName() != null && file.getName().endsWith(".obj")) { // 处理该文件 readObjFile(file.getUri()); } } } }
读取文件内容
private void readObjFile(Uri fileUri) { try (InputStream is = getContentResolver().openInputStream(fileUri); BufferedReader reader = new BufferedReader(new InputStreamReader(is))) { String line; StringBuilder content = new StringBuilder(); while ((line = reader.readLine()) != null) { content.append(line).append("\n"); } // 处理读取到的.obj内容 } catch (IOException e) { e.printStackTrace(); } }
写入文件到选中文件夹
private void writePackFile(Uri folderUri, String fileName, byte[] data) { DocumentFile rootFolder = DocumentFile.fromTreeUri(this, folderUri); if (rootFolder != null) { DocumentFile newFile = rootFolder.createFile("application/octet-stream", fileName); if (newFile != null) { try (OutputStream os = getContentResolver().openOutputStream(newFile.getUri())) { os.write(data); } catch (IOException e) { e.printStackTrace(); } } } }
注:createFile的MIME类型可以用application/octet-stream适配所有非媒体文件,或者根据具体格式指定对应类型。
避开MANAGE_EXTERNAL_STORAGE的注意事项
- 不要使用
MANAGE_EXTERNAL_STORAGE权限,除非你的应用是文件管理器类(Google Play审核严格限制该权限的使用场景)。 - 禁止直接用
FileAPI操作外部存储公共目录(如Environment.getExternalStorageDirectory()),Android 10+已限制此类操作,这也是你之前创建公共文件夹崩溃的原因。
常见问题排查
- 权限未持久化:必须调用
takePersistableUriPermission,否则重启应用后权限失效。 - 文件操作失败:确保申请权限时同时包含
READ和WRITE权限,且DocumentFile对象不为空。 - 初始路径无效:
EXTRA_INITIAL_URI只支持特定系统目录的Uri(如下载、图片等),自定义目录无法指定初始路径。
内容的提问来源于stack exchange,提问作者Михаил Куликовец
相关产品推荐
相关产品推荐

