Android 13+公共文档目录文本文件读写失败问题求助
问题原因分析
- Android 13+作用域存储的媒体类型限制:
WRITE_EXTERNAL_STORAGE权限在Android 13及以上版本中,仅授予对图片、音频、视频这类媒体文件的写入权限,文本文件不属于媒体范畴,因此无法通过旧的File API写入公共Documents目录。这就是PNG截图能正常写入,而文本文件失败的核心原因。 - 模拟器与真机的规则差异:Android模拟器默认可能开启调试兼容模式,放宽了作用域存储的限制,而真机严格执行官方的存储规则,导致功能表现不一致。
- 权限申请的异步问题:
askForWritePermission()是异步请求权限的方法,调用后立刻执行文件读写操作,此时权限可能还未完成授予,直接导致操作失败。
解决方案
根据需求提供三种可行方案:
方案一:使用存储访问框架(SAF)操作公共Documents目录
适合需要让用户在系统文件管理器中看到文本文件的场景,无需依赖WRITE_EXTERNAL_STORAGE权限:
保存文本文件到Documents/TEST目录
private static final int REQUEST_SAVE_TEXT = 1001; // 启动SAF选择保存路径 private void saveTextFile(String content, String month) { Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT); intent.addCategory(Intent.CATEGORY_OPENABLE); intent.setType("text/plain"); intent.putExtra(Intent.EXTRA_TITLE, month + ".txt"); // 指定初始目录为Documents/TEST Uri testDirUri = DocumentsContract.buildDocumentUriUsingTree( MediaStore.Downloads.EXTERNAL_CONTENT_URI, DocumentsContract.getTreeDocumentId(MediaStore.Downloads.EXTERNAL_CONTENT_URI) + "/TEST" ); intent.putExtra(DocumentsContract.EXTRA_INITIAL_URI, testDirUri); startActivityForResult(intent, REQUEST_SAVE_TEXT); } // 在回调中处理写入逻辑 @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (requestCode == REQUEST_SAVE_TEXT && resultCode == RESULT_OK) { Uri uri = data.getData(); try (OutputStream outputStream = getContentResolver().openOutputStream(uri)) { outputStream.write(content.getBytes(StandardCharsets.UTF_8)); } catch (IOException e) { e.printStackTrace(); } } }
读取文本文件
private static final int REQUEST_LOAD_TEXT = 1002; // 启动SAF选择要读取的文件 private void loadTextFile() { Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT); intent.addCategory(Intent.CATEGORY_OPENABLE); intent.setType("text/plain"); startActivityForResult(intent, REQUEST_LOAD_TEXT); } // 在回调中处理读取逻辑 @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (requestCode == REQUEST_LOAD_TEXT && resultCode == RESULT_OK) { Uri uri = data.getData(); try (InputStream inputStream = getContentResolver().openInputStream(uri)) { String content = new String(inputStream.readAllBytes(), StandardCharsets.UTF_8); writeDataFromFileToTable(content); } catch (IOException e) { e.printStackTrace(); } } }
方案二:使用应用专属存储
适合仅应用自身需要访问的文件,无需申请任何存储权限,安全性更高:
修改文件路径获取代码
public File getSavedFile(String month) { // 获取应用专属外部存储的Documents目录 File appTestDir = new File(getExternalFilesDir(Environment.DIRECTORY_DOCUMENTS), "TEST"); appTestDir.mkdirs(); return new File(appTestDir, month + ".txt"); } public File getSavedImage(String month) { // 获取应用专属外部存储的Pictures目录 File appTestDir = new File(getExternalFilesDir(Environment.DIRECTORY_PICTURES), "TEST"); appTestDir.mkdirs(); return new File(appTestDir, month + ".png"); }
清理Manifest权限
删除READ_EXTERNAL_STORAGE和WRITE_EXTERNAL_STORAGE权限,应用专属存储不需要这些权限。
方案三:申请MANAGE_EXTERNAL_STORAGE权限(仅特殊场景使用)
如果你的应用是文件管理器类应用,需要完全访问外部存储,可以申请这个权限,但该权限需要Google Play审核,不推荐普通应用使用:
权限配置
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />
申请权限代码
public void askForManageStoragePermission() { if (!Environment.isExternalStorageManager()) { Intent intent = new Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION); Uri uri = Uri.fromParts("package", getPackageName(), null); intent.setData(uri); startActivity(intent); } }
额外注意事项
- 移除
tools:ignore="ScopedStorage"标记,遵循Android官方存储规范,避免后续版本兼容性问题。 - 权限申请后,必须通过
onRequestPermissionsResult回调确认权限授予完成,再执行文件操作。
内容的提问来源于stack exchange,提问作者Vasil3
相关产品推荐
相关产品推荐

