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

Android 12下使用SAF修改其他应用私有目录文本文件的方案

使用SAF访问其他应用Android/data目录下文件的解决方案(Android 12+)

Android 11及以上系统收紧了对/storage/emulated/0/Android/data/目录的访问权限,即使申请了MANAGE_EXTERNAL_STORAGE权限,也无法直接通过路径访问其他应用的专属文件目录,必须通过**存储访问框架(SAF)**让用户手动授权目标文件或目录,才能进行读写操作。以下是完整的Java实现代码:

一、发起SAF文件选择请求

启动SAF文件选择器,引导用户定位并选择目标text.txt文件:

private static final int REQUEST_CODE_OPEN_DOCUMENT = 100;

// 触发SAF文件选择流程
private void openTargetFileWithSAF() {
    Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
    intent.addCategory(Intent.CATEGORY_OPENABLE);
    intent.setType("text/plain");
    
    // 预设初始目录为Android/data,帮助用户快速定位目标路径
    Uri initialAndroidDataUri = Uri.parse("content://com.android.externalstorage.documents/tree/primary%3AAndroid%2Fdata");
    intent.putExtra(DocumentsContract.EXTRA_INITIAL_URI, initialAndroidDataUri);
    
    startActivityForResult(intent, REQUEST_CODE_OPEN_DOCUMENT);
}

二、处理SAF授权结果

在回调中获取用户授权后的文件Uri,并申请持久化权限(避免重启后需重新授权):

兼容旧版的onActivityResult实现

@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    if (requestCode == REQUEST_CODE_OPEN_DOCUMENT && resultCode == RESULT_OK) {
        if (data != null && data.getData() != null) {
            Uri targetFileUri = data.getData();
            // 申请持久化读写权限
            getContentResolver().takePersistableUriPermission(targetFileUri,
                    Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
            
            // 执行读写操作
            writeContentToFile(targetFileUri, "需要写入的文本内容");
            String fileContent = readContentFromFile(targetFileUri);
        }
    }
}

AndroidX推荐的registerForActivityResult实现

private final ActivityResultLauncher<Intent> safFilePickerLauncher = registerForActivityResult(
        new ActivityResultContracts.StartActivityForResult(),
        result -> {
            if (result.getResultCode() == RESULT_OK && result.getData() != null) {
                Uri targetFileUri = result.getData().getData();
                if (targetFileUri != null) {
                    getContentResolver().takePersistableUriPermission(targetFileUri,
                            Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
                    
                    writeContentToFile(targetFileUri, "需要写入的文本内容");
                    String fileContent = readContentFromFile(targetFileUri);
                }
            }
        });

// 调用SAF选择器的入口方法
private void openTargetFileWithSAF() {
    Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
    intent.addCategory(Intent.CATEGORY_OPENABLE);
    intent.setType("text/plain");
    Uri initialAndroidDataUri = Uri.parse("content://com.android.externalstorage.documents/tree/primary%3AAndroid%2Fdata");
    intent.putExtra(DocumentsContract.EXTRA_INITIAL_URI, initialAndroidDataUri);
    safFilePickerLauncher.launch(intent);
}

三、SAF读写文件核心方法

写入文件内容

private void writeContentToFile(Uri fileUri, String content) {
    try (OutputStream outputStream = getContentResolver().openOutputStream(fileUri)) {
        if (outputStream != null) {
            outputStream.write(content.getBytes(StandardCharsets.UTF_8));
            outputStream.flush();
            Toast.makeText(this, "文件写入成功", Toast.LENGTH_SHORT).show();
        }
    } catch (IOException e) {
        e.printStackTrace();
        Toast.makeText(this, "写入失败:" + e.getMessage(), Toast.LENGTH_SHORT).show();
    }
}

读取文件内容

private String readContentFromFile(Uri fileUri) {
    StringBuilder contentBuilder = new StringBuilder();
    try (InputStream inputStream = getContentResolver().openInputStream(fileUri);
         BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8))) {
        String line;
        while ((line = reader.readLine()) != null) {
            contentBuilder.append(line).append("\n");
        }
    } catch (IOException e) {
        e.printStackTrace();
        Toast.makeText(this, "读取失败:" + e.getMessage(), Toast.LENGTH_SHORT).show();
    }
    return contentBuilder.toString();
}

关键注意事项

  • 权限持久化:takePersistableUriPermission方法可让App在重启后仍保持对目标文件的访问权限,无需用户重复授权。
  • 用户触发限制:SAF选择器必须由用户主动操作(如点击按钮)触发,不能自动弹出,否则会被系统拦截。
  • 批量操作需求:若需操作目标目录下多个文件,可替换为ACTION_OPEN_DOCUMENT_TREE请求目录授权,后续通过SAF遍历目录内文件。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.21 00:32:47