Android 10及以上版本中的WRITE_EXTERNAL_STORAGE权限处理
问题概述
在Android 10(API级别29)及更高版本中,android.permission.WRITE_EXTERNAL_STORAGE
权限的行为发生了重大变化。开发者在Android Studio中会遇到lint警告,提示此权限在定位Android 10及以上版本时将不再提供完全的写访问权限。
具体表现为:
- 在Android 10+设备上,即使移除该权限,应用仍可在内部存储的
Pictures/MY_APP_NAME
等目录保存文件 - 但在Android 9(API级别28)及更低版本中,缺少此权限会导致文件保存功能失效
- 这种差异导致需要支持多版本Android系统的开发者面临兼容性挑战
解决方案
1. 使用maxSdkVersion属性(推荐方案)
最有效的方法是在AndroidManifest.xml中为权限指定maxSdkVersion
属性:
xml
<uses-permission
android:name="android.permission.WRITE_EXTERNAL_STORAGE"
android:maxSdkVersion="28"
tools:ignore="ScopedStorage" />
TIP
如果您的AndroidManifest.xml
中尚未声明tools命名空间,请添加:
xml
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.app.my">
2. 运行时权限处理
在代码中根据API级别进行条件性权限请求:
kotlin
private fun hasWriteStoragePermission(): Boolean {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
// Android 10+ 不需要此权限
return true
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
// Android 6.0-9.0 需要动态请求权限
if (ActivityCompat.checkSelfPermission(activity!!,
Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
requestPermissions(
arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE),
REQUEST_PERMISSIONS_CODE_WRITE_STORAGE
)
return false
}
}
return true
}
3. Android 10的特殊处理(已过时)
注意
此方法仅在Android 10上有效,在Android 11+中已被废弃
xml
<application
android:requestLegacyExternalStorage="true">
Android 11+的新要求
从Android 11(API级别30)开始:
- WRITE_EXTERNAL_STORAGE权限完全无效,不再提供任何文件访问权限
- requestLegacyExternalStorage标志被忽略,不再有任何效果
- 需要MANAGE_EXTERNAL_STORAGE权限来访问共享存储空间:
xml
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />
- 引导用户启用所有文件访问权限:
kotlin
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R &&
!Environment.isExternalStorageManager()) {
val uri = Uri.parse("package:${BuildConfig.APPLICATION_ID}")
startActivity(Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION, uri))
}
重要警告
使用MANAGE_EXTERNAL_STORAGE权限需要向Google Play商店提供充分的技术理由,否则应用可能被拒绝上架。应优先考虑使用MediaStore API而不是申请此权限。
性能考虑
在Android 11+中,传统的File API现在只是一个包装器,内部委托给MediaStore处理。这意味着:
- 文件操作速度会比直接使用MediaStore API慢2-3倍
- 建议在新项目中优先使用MediaStore API进行文件操作
版本兼容性总结
Android版本 | WRITE_EXTERNAL_STORAGE | requestLegacyExternalStorage | 推荐方案 |
---|---|---|---|
≤ Android 9 | 必需 | 无效 | 正常请求权限 |
Android 10 | 可选(需配合requestLegacyExternalStorage) | 有效 | 使用maxSdkVersion="29" |
≥ Android 11 | 完全无效 | 无效 | 使用MediaStore API或MANAGE_EXTERNAL_STORAGE |
最佳实践建议
- 优先使用应用专属目录:使用
Context.getExternalFilesDir()
访问应用专属外部存储 - 使用MediaStore API:对于媒体文件,使用MediaStore API而不是直接文件路径
- 使用Storage Access Framework:对于用户选择的文件,使用SAF提供的 Intent
- 最小化权限请求:只在绝对必要时请求文件访问权限
通过遵循这些指导原则,您可以确保应用在所有Android版本上都能正确处理文件存储,同时符合最新的平台安全要求。