Android SDK31创建外置存储文件报Operation not permitted错误
问题根因
该报错由Android 11(API 30)及以上版本强制启用的**分区存储(Scoped Storage)**机制导致:
- 当
compileSdkVersion与targetSdkVersion均设置为31(即Android 12)时,旧版的WRITE_EXTERNAL_STORAGE、READ_EXTERNAL_STORAGE权限不再授予外置存储根目录、其他应用专属目录的直接读写权限,你代码中通过Environment.getExternalStorageDirectory()访问根路径创建文件的操作会被系统直接拦截,抛出IOException: Operation not permitted异常。 - 你之前配置的静态权限声明、API23+动态权限申请逻辑,仅适配了Android 6.0到Android 10的存储规则,没有覆盖API30+的存储权限变更。
适配方案
根据你的文件使用场景,选择对应方案即可:
方案1:使用应用专属外置目录(无权限要求,优先推荐)
如果存储的文件不需要给其他应用访问、不需要在应用卸载后保留,直接使用应用专属的外置存储目录即可,无需申请任何存储权限,系统不会拦截该目录下的文件操作:
// 目录路径为 /sdcard/Android/data/你的应用包名/files/ File file = new File(getExternalFilesDir(null), "dummy.txt"); if (!file.exists()){ file.createNewFile(); }
常用的专属外置目录还有两个:
- 缓存目录:调用
context.getExternalCacheDir()获取,对应路径/sdcard/Android/data/你的应用包名/cache/,系统存储空间不足时会自动清理该目录下的文件 - 持久化文件目录:即上述
getExternalFilesDir()目录,应用卸载时该目录下的所有文件会被系统同步删除
方案2:访问系统公共目录(适合共享类文件)
如果需要将文件存放到系统公共目录(如DCIM、Pictures、Download、Documents等),供其他应用访问或者在卸载后保留,不需要申请存储权限,通过系统提供的API操作即可:
- 图片、视频、音频类媒体文件:通过
MediaStoreAPI将文件插入到对应公共媒体集合 - 文档、安装包等非媒体文件:通过存储访问框架(SAF)拉起系统文件选择器,让用户指定保存位置,拿到返回的Uri后通过
ContentResolver写入文件
方案3:临时关闭分区存储(仅作过渡兼容,不推荐)
如果短时间内无法完成分区存储适配,可以在Manifest清单文件的<application>标签下添加如下属性,临时启用旧版存储访问模式,该配置在targetSdk 31版本上仍可生效,但后续高版本Android系统会逐步移除该兼容项:
<application android:requestLegacyExternalStorage="true" <!-- 其余原有属性 --> > <!-- 原有组件声明 --> </application>
注意:该方案仅适合版本迭代过渡期使用,主流应用市场在审核高targetSdk版本应用时,会逐步要求强制适配分区存储,不建议长期依赖该配置。
方案4:申请全存储访问权限(仅适合特定功能场景)
如果你的应用是文件管理器、数据备份、杀毒类工具,确实需要访问外置存储全目录的所有文件,可以申请特殊的MANAGE_EXTERNAL_STORAGE权限:
- 先在Manifest中添加权限声明:
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />
- 动态判断权限状态,未授权时引导用户跳转到系统设置页开启:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { if (!Environment.isExternalStorageManager()) { // 未授权,跳转到系统所有文件访问授权页 Intent intent = new Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION); intent.setData(Uri.parse("package:" + getPackageName())); startActivityForResult(intent, REQUEST_CODE_MANAGE_STORAGE); } else { // 已获得全存储访问权限,可以正常操作根目录文件 File file = new File(Environment.getExternalStorageDirectory(), "dummy.txt"); if (!file.exists()){ file.createNewFile(); } } }
注意:该权限属于特殊敏感权限,如果应用没有合理的全存储访问需求,提交应用市场审核时会被直接拒绝。
内容的提问来源于stack exchange,提问作者tushar
相关产品推荐
相关产品推荐

