WRITE_EXTERNAL_STORAGE权限报错:replace、legacy作用及不使用二者的解决方案
两个属性的具体作用
tools:node="replace"
这是Android Manifest清单合并规则的配置项。构建App时,构建工具会把主模块、所有引入依赖库的AndroidManifest文件合并为最终的清单文件。如果多个依赖库也声明了WRITE_EXTERNAL_STORAGE权限,部分库会给这个权限加上android:maxSdkVersion=28之类的版本限制,默认合并逻辑会把这些限制属性带到最终的权限声明里,导致Android 9以上系统上这个权限直接被判定为不需要,申请时会直接被拒绝。加这个属性就是告诉构建工具,主模块声明的这条权限规则完全替换所有依赖里的同一项权限声明,不会合并其他库附带的限制属性。android:requestLegacyExternalStorage="true"
这是Android 10(API 29)推出的分区存储临时兼容开关,注意这个属性的正确位置是写在<application>标签上,不是写在uses-permission权限声明上,加在权限标签上完全不会生效。它的作用是在Android 10系统上临时关闭分区存储机制,让App沿用旧版的外部存储全量访问逻辑,在Android 11(API 30)及以上系统上这个开关已经失效,系统会强制启用分区存储,加了也不会有效果。
不使用上述两个属性的权限问题解决方案
你遇到的权限无法授予问题,本质上要么是清单合并时权限被依赖库加了高版本限制,要么是没有适配新版系统的存储权限规则,完全可以不用这两个属性解决,按以下步骤处理即可:
- 第一步:排查合并后的清单文件,移除不必要的权限版本限制
打开Android Studio中主模块的AndroidManifest.xml,切换到底部的「Merged Manifest」 tab,搜索WRITE_EXTERNAL_STORAGE查看最终合并生成的权限声明,如果发现声明上带了android:maxSdkVersion属性且数值小于你当前适配的系统版本,不需要加replace属性,直接在主模块的权限声明上明确指定android:maxSdkVersion="32"即可——因为从Android 13(API 33)开始,WRITE_EXTERNAL_STORAGE权限已经被正式废弃,在33及以上系统上声明这个权限不会有任何效果。 - 第二步:适配分区存储规则,不要依赖全局写存储权限
现在各大应用商店都要求targetSdk升级到33以上,旧的全量外部存储访问模式已经被淘汰,按你的实际使用场景选对应的访问方式即可,根本不需要依赖legacy开关:- 如果是读写App自身的私有目录(比如
/Android/data/你的应用包名/下的文件):不需要申请任何存储权限,直接通过context.getExternalFilesDir()、context.getExternalCacheDir()等API获取路径即可正常读写,全系统版本通用。 - 如果是读写公共媒体文件(相册图片、视频、音频):API 32及以下需要修改/删除媒体文件时才需要申请
WRITE_EXTERNAL_STORAGE;API 33及以上申请对应类型的READ_MEDIA_IMAGES/READ_MEDIA_VIDEO/READ_MEDIA_AUDIO权限即可,写入公共媒体库通过MediaStore API操作不需要申请写权限。 - 如果是需要访问用户指定的任意文件/文件夹:使用系统自带的存储访问框架(SAF),拉起系统文件选择器让用户手动授权对应路径的访问权限即可,不需要申请全局存储权限,全版本兼容。
- 如果是文件管理器、备份工具这类确实需要访问全量外部存储的App:可以申请
MANAGE_EXTERNAL_STORAGE权限,跳转系统设置页引导用户开启「所有文件访问权限」,普通功能类App不建议申请这个权限,大概率会被应用商店拒审。
- 如果是读写App自身的私有目录(比如
- 第三步:补全运行时动态申请逻辑
WRITE_EXTERNAL_STORAGE属于危险权限,从Android 6.0(API 23)开始,不能只在Manifest里声明权限,必须在对应功能触发时,通过运行时权限API弹窗向用户申请,用户手动同意后才能拿到权限,否则会直接判定权限被拒绝。
内容的提问来源于stack exchange,提问作者c-an
相关产品推荐
相关产品推荐

