Android 11下应用重装后无法获取MediaStore创建的文件如何解决
问题根因
该问题是Android 11(API 30)引入的存储权限变更带来的固有行为:
- 应用通过
MediaStore写入公共共享目录(如Download、DCIM、Pictures等)的文件,默认会被标记为当前应用的归属所有者,文件的OWNER_PACKAGE_NAME字段会写入你应用的包名 - 应用卸载时,系统会清空你应用对应创建的所有MediaStore文件的归属标记,同时移除原有应用和这些文件的读写权限关联
- 应用重装后,哪怕包名、签名和卸载前完全一致,也会被识别为全新的应用实例,不再是这些旧文件的登记所有者,因此默认无法直接通过
MediaStore检索到这些文件
解决方案
方案1:申请MANAGE_EXTERNAL_STORAGE权限(仅适用于核心功能为文件管理的应用)
注意:Google Play对该权限的审核规则非常严格,非文件管理类应用申请该权限大概率会被拒上架,请谨慎使用
- 首先在
AndroidManifest.xml中添加权限声明:
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />
- 运行时引导用户跳转到系统设置页手动开启该权限,获取权限后即可正常查询所有公共目录下的MediaStore文件
方案2:调整MediaStore查询参数(普通应用首选)
如果你的应用不需要全文件管理权限,按如下步骤调整即可:
- 先申请对应的存储权限:API 30~32申请
READ_EXTERNAL_STORAGE权限,API 33及以上根据你访问的文件类型申请对应分类媒体权限(READ_MEDIA_IMAGES/READ_MEDIA_VIDEO/READ_MEDIA_AUDIO) - 查询MediaStore时在筛选条件中添加无主文件匹配规则,示例查询代码(以查询公共目录下的图片文件为例):
val projection = arrayOf(MediaStore.Images.Media._ID, MediaStore.Images.Media.DISPLAY_NAME, MediaStore.Images.Media.RELATIVE_PATH) // 同时匹配当前应用新建的文件 + 之前卸载后遗留的无主文件 val selection = "${MediaStore.Images.Media.OWNER_PACKAGE_NAME} IS NULL OR ${MediaStore.Images.Media.OWNER_PACKAGE_NAME} = ?" val selectionArgs = arrayOf(packageName) contentResolver.query( MediaStore.Images.Media.EXTERNAL_CONTENT_URI, projection, selection, selectionArgs, "${MediaStore.Images.Media.DATE_ADDED} DESC" )?.use { cursor -> // 遍历cursor处理查询结果 }
方案3:写入文件时提前做兼容处理
如果还没上线,可以在写入文件时就做好适配,避免后续卸载重装出现问题:
- 写入文件流程完成后,立即将文件的
IS_PENDING标记从1改为0,确保文件可以被系统和其他应用正常识别 - 对于重要的核心文件,可以在首次写入完成后请求系统的持久化Uri权限,用户授权后哪怕应用重装,只要你保留了对应的Uri,依然可以直接访问该文件
注意事项
- 不要尝试绕过系统存储权限规范,直接访问公共目录的原始文件路径,Android 11及以上系统会直接拦截该类请求
- 如果你不希望应用卸载后留下无主文件,可以注册
android.intent.action.PACKAGE_FULLY_REMOVED广播,监听到自身卸载事件时主动清理之前创建的公共目录文件
内容的提问来源于stack exchange,提问作者Davide Murru
相关产品推荐
相关产品推荐

