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

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审核严格限制该权限的使用场景)。
  • 禁止直接用File API操作外部存储公共目录(如Environment.getExternalStorageDirectory()),Android 10+已限制此类操作,这也是你之前创建公共文件夹崩溃的原因。

常见问题排查

  1. 权限未持久化:必须调用takePersistableUriPermission,否则重启应用后权限失效。
  2. 文件操作失败:确保申请权限时同时包含READ和WRITE权限,且DocumentFile对象不为空。
  3. 初始路径无效:EXTRA_INITIAL_URI只支持特定系统目录的Uri(如下载、图片等),自定义目录无法指定初始路径。

内容的提问来源于stack exchange,提问作者Михаил Куликовец

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.14 00:03:33