Android 11使用getExternalStoragePublicDirectory可建目录无法建文件问题
问题根因分析
该问题是Android 11 分区存储(Scoped Storage)的权限规则限制导致的,和你是否申请存储权限没有直接关系:
从Android 11开始,系统对公共媒体目录(DIRECTORY_MOVIES、DIRECTORY_PICTURES、DIRECTORY_MUSIC等)的File API访问做了细粒度限制,WRITE_EXTERNAL_STORAGE权限仅能让你读写自己应用创建的媒体文件,无法直接创建非对应类型的文件。
1. 可创建目录、不可创建文件的原因
- 系统允许应用在公共媒体目录下创建子目录,但不允许通过
File.createNewFile()直接创建和目录类型不匹配的文件。你测试的文件名newFileTest没有视频格式后缀,不属于MOVIES目录接受的媒体文件类型,所以被系统拦截。 DIRECTORY_DOCUMENTS属于文档类公共目录,规则宽松允许创建任意格式的普通文件,所以你切换到该目录后操作可以正常执行。- Android 10的分区存储规则还未做该细粒度限制,所以授予存储权限后可以正常运行。
2. canRead()/canWrite()返回true但操作失败的原因
canRead()和canWrite()仅校验Linux层的文件权限位,Android 11的分区存储拦截逻辑是在虚拟文件系统层实现的,不会修改传统的文件权限位,所以这两个方法的返回结果和实际可操作权限不一致,属于系统已知的设计行为。- 你捕获到的
No such file or directory是系统拦截操作后返回的通用错误,并非父目录不存在。
解决方案
方案1:添加对应类型的文件后缀
如果要使用File API在MOVIES目录下创建文件,给文件名加上视频格式后缀即可正常创建:
val newFile = File(externalMoviesPath, "newFileTest.mp4") newFile.createNewFile() // 执行成功
方案2:使用官方推荐的MediaStore API
该方式适配所有Android 10+系统,不受文件后缀限制:
val contentValues = ContentValues().apply { put(MediaStore.Video.Media.DISPLAY_NAME, "newFileTest") put(MediaStore.Video.Media.RELATIVE_PATH, Environment.DIRECTORY_MOVIES) put(MediaStore.Video.Media.MIME_TYPE, "video/mp4") } // 插入后会返回对应的文件Uri,可通过Uri读写文件内容 contentResolver.insert(MediaStore.Video.Media.EXTERNAL_CONTENT_URI, contentValues)
方案3:临时兼容方案(仅适用于非Google Play上架应用)
在AndroidManifest.xml的application节点添加android:requestLegacyExternalStorage="true"可以临时绕过分区存储限制,但该配置在Android 12及以上版本已经失效,且不符合Google Play的上架要求,不建议长期使用。
内容的提问来源于stack exchange,提问作者t0m
相关产品推荐
相关产品推荐

