Android 10及以上版本如何创建根目录,现有代码创建失败如何解决
失败原因
Android 10(API 级别29)开始系统引入分区存储机制,默认禁止应用直接访问共享存储根目录,原有代码中使用的Environment.getExternalStorageDirectory()已被官方废弃,普通应用无权限直接在根目录创建自定义文件夹,因此调用dir.mkdir()会返回false,创建失败。
另外你提到的“使用Cursor创建根文件夹”存在认知偏差:Cursor是Android系统中用于查询数据的工具类,本身无法直接创建文件夹,你需要的是适配分区存储规则,使用MediaStore API向公共媒体目录写入文件,该方案兼容Android 10及以上所有版本,无需申请特殊权限。
前置配置
先在AndroidManifest.xml中补充必要配置:
<!-- Android 12及以下版本存储权限 --> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="32" /> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="32" /> <!-- Android 13及以上版本无需存储权限即可写入公共相册 -->
如果你的targetSdkVersion <=29,可以在application节点临时添加android:requestLegacyExternalStorage="true"作为过渡方案,但该配置在Android 11及以上系统无效,建议直接使用下方适配方案
代码修改方案
你可以直接替换AsyncTask中doInBackground的原有逻辑,使用MediaStore写入图片:
@Override protected Void doInBackground(Bitmap... bitmaps) { ContentResolver resolver = getContentResolver(); ContentValues contentValues = new ContentValues(); // 设置文件显示名称 contentValues.put(MediaStore.Images.Media.DISPLAY_NAME, String.format("%d.jpg", System.currentTimeMillis())); // 设置文件类型 contentValues.put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg"); // 指定存储到公共Pictures目录下的SaveImage文件夹,系统会自动创建该文件夹 contentValues.put(MediaStore.Images.Media.RELATIVE_PATH, Environment.DIRECTORY_PICTURES + "/SaveImage"); // 设置文件状态为待处理,写入完成前其他应用无法访问 contentValues.put(MediaStore.Images.Media.IS_PENDING, 1); // 插入到媒体库,得到文件Uri Uri imageUri = resolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues); if (imageUri == null) { return null; } try { OutputStream outputStream = resolver.openOutputStream(imageUri); if (outputStream != null) { bitmapVar.compress(Bitmap.CompressFormat.JPEG, 100, outputStream); outputStream.close(); } // 写入完成后更新状态,允许其他应用访问该图片 contentValues.clear(); contentValues.put(MediaStore.Images.Media.IS_PENDING, 0); resolver.update(imageUri, contentValues, null, null); } catch (IOException e) { e.printStackTrace(); } return null; }
该方案不需要你手动创建文件夹,系统会自动在公共Pictures目录下生成SaveImage文件夹,图片保存后可以在系统相册中直接查看,兼容所有Android 10及以上版本。
补充说明:AsyncTask已在Android API 30中被废弃,后续迭代建议替换为Coroutines或者WorkManager实现异步任务
内容的提问来源于stack exchange,提问作者Faagu Sangani

