Skip to content

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)开始:

  1. WRITE_EXTERNAL_STORAGE权限完全无效,不再提供任何文件访问权限
  2. requestLegacyExternalStorage标志被忽略,不再有任何效果
  3. 需要MANAGE_EXTERNAL_STORAGE权限来访问共享存储空间:
xml
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />
  1. 引导用户启用所有文件访问权限:
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_STORAGErequestLegacyExternalStorage推荐方案
≤ Android 9必需无效正常请求权限
Android 10可选(需配合requestLegacyExternalStorage)有效使用maxSdkVersion="29"
≥ Android 11完全无效无效使用MediaStore API或MANAGE_EXTERNAL_STORAGE

最佳实践建议

  1. 优先使用应用专属目录:使用Context.getExternalFilesDir()访问应用专属外部存储
  2. 使用MediaStore API:对于媒体文件,使用MediaStore API而不是直接文件路径
  3. 使用Storage Access Framework:对于用户选择的文件,使用SAF提供的 Intent
  4. 最小化权限请求:只在绝对必要时请求文件访问权限

通过遵循这些指导原则,您可以确保应用在所有Android版本上都能正确处理文件存储,同时符合最新的平台安全要求。