Android 10(小米9)下无法在Environment.DIRECTORY_DOCUMENTS创建子目录
问题原因解析
1. Android版本存储规则差异
- Android 11(API30):谷歌调整了分区存储策略,允许应用直接在
Environment.DIRECTORY_DOCUMENTS等系统公共目录下创建自定义子目录,只要持有WRITE_EXTERNAL_STORAGE权限即可,这就是代码在Android11设备上正常工作的原因。 - Android 10(API29):严格执行分区存储规则(当targetSdk>=29时默认启用),应用无法直接在系统公共目录(如Documents)下创建自定义子目录,必须通过**存储访问框架(SAF)**请求用户授权,或者使用
MediaStoreAPI操作文件。
2. 小米设备的定制化限制
部分国内厂商(如小米)的Android10定制系统,对公共Documents目录额外增加了权限校验,即使手动创建了Documents目录,应用也没有足够权限在其下创建子目录,导致mkdirs()始终返回false。
解决方案
方案一:启用遗留存储兼容(过渡方案)
如果暂时不想重构代码,可以在AndroidManifest.xml的<application>标签中添加以下属性,让应用在Android10设备上使用旧的存储模式:
android:requestLegacyExternalStorage="true"
注意:该属性仅在targetSdk<=30时生效,当targetSdk升级到31及以上时会被忽略,属于临时兼容方案。
方案二:适配存储访问框架(SAF)(推荐长期方案)
对于Android10及以上设备,使用SAF让用户手动授权访问Documents目录,然后通过Uri创建子目录:
- 发起SAF授权请求:
private static final int REQUEST_CODE_DOCUMENTS_DIR = 1001; public void requestDocumentsDirAccess() { Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE); intent.putExtra(DocumentsContract.EXTRA_INITIAL_URI, Uri.parse("content://com.android.externalstorage.documents/tree/primary%3ADocuments")); startActivityForResult(intent, REQUEST_CODE_DOCUMENTS_DIR); }
- 在
onActivityResult中处理授权结果并创建子目录:
@Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (requestCode == REQUEST_CODE_DOCUMENTS_DIR && resultCode == RESULT_OK) { Uri treeUri = data.getData(); // 持久化Uri权限,重启应用后仍可访问 getContentResolver().takePersistableUriPermission(treeUri, Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION); // 创建子目录MySubfolder DocumentsContract.createDocument(getContentResolver(), treeUri, DocumentsContract.Document.MIME_TYPE_DIR, "MySubfolder"); } }
方案三:调整目录策略
如果不需要让用户在Documents目录下看到文件,可以改用应用专属的外部存储目录,无需额外权限:
File dir = new File(getExternalFilesDir(Environment.DIRECTORY_DOCUMENTS), "MySubfolder"); dir.mkdirs();
该目录属于应用私有空间,卸载应用时会被删除,但不受分区存储限制,也无需申请存储权限。
代码优化建议
针对不同Android版本做分支处理,避免不必要的异常:
public static File getWriteableUserDirPath(Context context) { String folderName = "MySubfolder"; File dir = null; // 优先尝试应用专属外部目录(无权限问题) dir = new File(context.getExternalFilesDir(Environment.DIRECTORY_DOCUMENTS), folderName); if (dir.mkdirs() || dir.exists()) { return dir; } // 针对Android 11及以上,尝试公共Documents目录 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { try { dir = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS), folderName); if (dir.mkdirs() || dir.exists()) { return dir; } } catch (Exception e) { e.printStackTrace(); } } // 针对Android 10及以下,返回null让上层触发SAF授权 if (Build.VERSION.SDK_INT == Build.VERSION_CODES.Q) { return null; } // Android 9及以下,尝试旧的根目录方式 try { dir = Environment.getExternalStoragePublicDirectory(folderName); if (dir.mkdirs() || dir.exists()) { return dir; } } catch (Exception e) { e.printStackTrace(); } return null; }
内容的提问来源于stack exchange,提问作者JustAMartin
相关产品推荐
相关产品推荐

