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
相关产品推荐
相关产品推荐

