Android 14中如何获取外部存储Documents文件夹的读写权限?
Android 14 下访问Documents文件夹备份文件的实现方案
核心思路
Android 14 中,若无法申请MANAGE_APP_ALL_FILES_ACCESS_PERMISSION,应使用**存储访问框架(SAF,Storage Access Framework)**访问Documents文件夹。这是官方推荐的共享存储访问方式,无需特殊权限,通过用户手动选择文件夹/文件获取访问权限,完全符合系统权限规范。
具体实现步骤
1. 启动SAF选择Documents文件夹
调用系统文件选择器,引导用户选择Documents文件夹,获取文件夹的Uri权限:
// 定义请求码,用于回调识别 private static final int REQUEST_CODE_PICK_DOCUMENTS = 1001; // 启动文件夹选择流程 private void launchDocumentsPicker() { Intent pickerIntent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE); // 可选:默认定位到Documents文件夹(Android 11+支持) Uri initialDir = MediaStore.Downloads.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY); pickerIntent.putExtra(DocumentsContract.EXTRA_INITIAL_URI, initialDir); startActivityForResult(pickerIntent, REQUEST_CODE_PICK_DOCUMENTS); }
2. 处理选择结果并持久化权限
在onActivityResult(或AndroidX的registerForActivityResult)中获取选中文件夹的Uri,申请持久化权限确保后续无需重复选择:
@Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (requestCode == REQUEST_CODE_PICK_DOCUMENTS && resultCode == RESULT_OK) { if (data != null) { Uri folderUri = data.getData(); // 持久化读写权限,直到用户手动撤销 getContentResolver().takePersistableUriPermission( folderUri, Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION ); // 将Uri保存到SharedPreferences,方便后续直接使用 saveFolderUri(folderUri.toString()); // 此时已具备Documents文件夹的读写权限 } } } // 辅助方法:保存Uri到本地存储 private void saveFolderUri(String uriStr) { SharedPreferences prefs = getSharedPreferences("AppStoragePrefs", MODE_PRIVATE); prefs.edit().putString("selected_documents_uri", uriStr).apply(); }
3. 读写Documents文件夹内的备份文件
通过已保存的Uri,使用ContentResolver完成文件读写操作:
// 从SharedPreferences获取已保存的文件夹Uri private Uri getSavedFolderUri() { SharedPreferences prefs = getSharedPreferences("AppStoragePrefs", MODE_PRIVATE); String uriStr = prefs.getString("selected_documents_uri", null); return uriStr != null ? Uri.parse(uriStr) : null; } // 创建备份文件到Documents文件夹 private void createBackup(String backupFileName) { Uri folderUri = getSavedFolderUri(); if (folderUri == null) { // 未选择文件夹,重新启动选择流程 launchDocumentsPicker(); return; } // 构建备份文件的Uri Uri backupFileUri = DocumentsContract.buildDocumentUriUsingTree( folderUri, DocumentsContract.getTreeDocumentId(folderUri) + "/" + backupFileName ); try (OutputStream outputStream = getContentResolver().openOutputStream(backupFileUri)) { // 写入备份内容(示例为字符串,实际可替换为文件流) String backupContent = "备份数据内容"; outputStream.write(backupContent.getBytes()); Toast.makeText(this, "备份完成", Toast.LENGTH_SHORT).show(); } catch (IOException e) { e.printStackTrace(); Toast.makeText(this, "备份失败", Toast.LENGTH_SHORT).show(); } } // 读取Documents文件夹内的备份文件 private String readBackup(String backupFileName) { Uri folderUri = getSavedFolderUri(); if (folderUri == null) { launchDocumentsPicker(); return null; } Uri backupFileUri = DocumentsContract.buildDocumentUriUsingTree( folderUri, DocumentsContract.getTreeDocumentId(folderUri) + "/" + backupFileName ); try (InputStream inputStream = getContentResolver().openInputStream(backupFileUri)) { byte[] buffer = new byte[inputStream.available()]; inputStream.read(buffer); return new String(buffer); } catch (IOException e) { e.printStackTrace(); return null; } }
4. Android 14 适配注意事项
- Android 14 无需申请
READ_EXTERNAL_STORAGE或WRITE_EXTERNAL_STORAGE权限,SAF操作完全基于用户授予的Uri权限。 - 若仅需访问单个备份文件,可使用
Intent.ACTION_OPEN_DOCUMENT替代文件夹选择,流程逻辑一致。 - 持久化的Uri权限会一直保留,直到用户在系统设置中主动撤销应用的存储访问权限。
内容的提问来源于stack exchange,提问作者Daniel Román
相关产品推荐
相关产品推荐

