Android 10+下无需MANAGE_EXTERNAL_STORAGE创建DCIM/Download空子目录求示例
解决方案:Android 10+ 分区存储下创建DCIM/Download子目录
一、利用MediaStore间接创建空子目录
MediaStore本身没有直接创建空目录的接口,因为分区存储中目录是随媒体文件自动生成的。可以通过创建临时媒体文件再删除的方式间接生成目录:
示例代码(Kotlin)
import android.content.ContentValues import android.content.Context import android.net.Uri import android.os.Build import android.provider.MediaStore import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext suspend fun createEmptySubDirInDCIM(context: Context, subDirName: String): Boolean { return withContext(Dispatchers.IO) { val contentValues = ContentValues().apply { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { put(MediaStore.Images.Media.RELATIVE_PATH, "DCIM/$subDirName") put(MediaStore.Images.Media.DISPLAY_NAME, "temp_file.jpg") put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg") } } val uri = context.contentResolver.insert( MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues ) ?: return@withContext false // 删除临时文件,保留目录 context.contentResolver.delete(uri, null, null) return@withContext true } }
说明
- 针对Android 10+(API 29+),通过
RELATIVE_PATH指定DCIM下的子目录,插入一个临时图片文件后立即删除,系统会保留创建的空目录。 - targetSDK33下无需额外静态权限,系统自动授予媒体文件写入权限。
- 修改
RELATIVE_PATH为"Download/$subDirName"即可创建Download下的子目录。
二、SAF方式创建子目录(需用户手动授权)
如果需要直接通过SAF创建目录,需引导用户选择父目录(如DCIM或Download),再通过DocumentFile创建子目录:
示例代码(Kotlin)
1. 启动SAF选择父目录
import android.app.Activity import android.content.Intent import android.os.Build import android.provider.DocumentsContract const val REQUEST_CODE_PICK_PARENT_DIR = 1001 fun startSAFParentDirPicker(activity: Activity) { val intent = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE).apply { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { // 默认定位到DCIM目录 val dcimUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI putExtra(DocumentsContract.EXTRA_INITIAL_URI, dcimUri) } } activity.startActivityForResult(intent, REQUEST_CODE_PICK_PARENT_DIR) }
2. 处理SAF返回结果并创建子目录
import android.content.ContentResolver import android.net.Uri import androidx.documentfile.provider.DocumentFile fun createSubDirViaSAF(contentResolver: ContentResolver, parentUri: Uri, subDirName: String): Boolean { val parentDoc = DocumentFile.fromTreeUri(contentResolver, parentUri) ?: return false val subDir = parentDoc.createDirectory(subDirName) return subDir != null } // 在Activity的onActivityResult中处理 override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { super.onActivityResult(requestCode, resultCode, data) if (requestCode == REQUEST_CODE_PICK_PARENT_DIR && resultCode == RESULT_OK) { data?.data?.let { parentUri -> // 保留长期访问权限 contentResolver.takePersistableUriPermission( parentUri, Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION ) val success = createSubDirViaSAF(contentResolver, parentUri, "MyAppDir") // 根据success状态处理结果提示 } } }
说明
- SAF需要用户手动确认父目录,但可通过
EXTRA_INITIAL_URI默认定位到目标目录,减少操作步骤。 - 调用
takePersistableUriPermission可保留对目录的长期访问权限,避免重复授权。
内容的提问来源于stack exchange,提问作者progquester
相关产品推荐
相关产品推荐

