针对 Android 10 时的 WRITE_EXTERNAL_STORAGE

Posted

技术标签:

【中文标题】针对 Android 10 时的 WRITE_EXTERNAL_STORAGE【英文标题】:WRITE_EXTERNAL_STORAGE when targeting Android 10 【发布时间】:2021-01-21 01:27:47 【问题描述】:

在 AS 中有一个关于 android.permission.WRITE_EXTERNAL_STORAGE 的 lint 警告。警告说,当面向 Android 10 及更高版本时,该权限将不再提供写入权限。删除上述权限仍然可以写入内部存储文件夹Pictures/MY_APP_NAME 以保存图像,但它仅适用于 Android 10 (SDK 29) 和/或更高版本(尚未在 Android R 上测试)。当我在 Android M (SDK 23) 等较低版本上再次对其进行测试时,保存图像停止工作,因此我决定返回 android.permission.WRITE_EXTERNAL_STORAGE,因此再次出现警告。棉绒是否可能只是误报,在不同情况下错误地诊断出问题?因为目前我的支持 SDK 从 21 开始到最新的 30,但 lint 仅指出在针对 Android 10 (SDK 29) 时不再需要它,并且没有考虑回顾项目的最低 SDK 支持。

【问题讨论】:

can still write in internal storage folder Pictures/MY_APP_NAME 奇怪。请给出文件夹的完整路径。 您仍然可以在 Android 10 中使用 MediaStore、ContentResolver 和 ContentValues 写入 Internal Storage/Pictures/MY_APP_NAME @SiddharthKamaria 是的,我尝试添加 android:maxSdkVersion="29" 但没有删除警告。 @MihaeKheel 很奇怪,即使没有 maxSdkVersion,我的 AS 也没有向我显示 WRITE_EXTERNAL_STORAGE 的 lint 警告。也许尝试重建或使缓存无效? @SiddharthKamaria 感谢您的指出,但我已经尝试过无效化、清理构建和重建警告仍然存在。也许是因为我们有不同的 AS 版本,我还是在 Canary 版本的 AS 中。 【参考方案1】:

尝试将其添加到您的清单中:

<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:requestLegacyExternalStorage="true" //Add this Line
android:label="@string/app_name">

 ---------<Activity, Sevices or Recivers>----------

</application>

并再次删除READ_EXTERNAL_STORAGE 权限:

<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

【讨论】:

不再需要 requestLegacyExternalStorage 并且对于 Android 10 来说不是一个好的解决方案,因为如果您想将文件存储在 Internal Storage/Pictures/MY_APP_NAME 中,可以使用 MediaStore、ContentResolver 和 ContentValues 等 API 没有。这是一个很好的解决方案。您可以继续使用经典文件路径。在 Android 11 上,您也可以仅使用经典文件路径写入图片文件夹。不需要 mediastore 和 saf @blackapps 请阅读developer.android.com/training/data-storage/use-cases Caution: After you update your app to target Android 11 (API level 30), the system ignores the requestLegacyExternalStorage attribute when your app is running on Android 11 devices, so your app must be ready to support scoped storage and to migrate app data for users on those devices. 另见***.com/questions/63364476/… 是的,旧版请求对 Android 11 设备没有任何作用。但它仍然适用于 Android 10 设备。所以继续使用它我会说,这就是我所说的。【参考方案2】:

解决方法是实际上忽略警告,因为它只是提供信息,因此无害。通过将 ma​​xSdkVersion 设置为 28 无需再担心了。

<uses-permission
    android:name="android.permission.WRITE_EXTERNAL_STORAGE"
    android:maxSdkVersion="28"
    tools:ignore="ScopedStorage" />

请注意,使用其他答案中所述的 android:requestLegacyExternalStorage 标志不是解决方案,只是一个临时补丁,在 Android 11 (API 30) 中将不再有效,以及未来的版本

更新,澄清一些开发者在 cmets 中表现出的疑惑和困惑:

如果在 Android 10 (API 29) 中使用 requestLegacyExternalStorage 标志,则照常请求 WRITE_EXTERNAL_STORAGE 权限。

标志 requestLegacyExternalStorageAndroid 11 (API 30) 中没有任何作用,它被完全忽略,并且没有解决方法。

WRITE_EXTERNAL_STORAGEAndroid 11 (API 30) 中不提供任何权限,它什么都不做,因此在 API 11 中您需要将 ma​​xSdkVersion 设置为 29

如果在 Android 10 (API 29) 中您也没有使用 requestLegacyExternalStorage,则将 ma​​xSdkVersion 设置为 28而不是 29

Android 11 (API 30) 开始,旧的 File API 可以再次使用,但“仅”在访问公共“共享存储”文件夹(DCIM、音乐等),或您的应用程序“私人”目录。对于其他位置,DocumentFile API 是必需的。

请考虑,Android 11 (API 30) 中的 File API 现在要慢得多,因为已被重构为本质上的包装器。这是为了强制其仅在允许的位置使用。因此,它不再是一个快速的系统文件 API,而只是一个在内部将工作委托给 MediaStore 的包装器。在 Android 11 或更高版本中使用 File API 时,您应该考虑性能损失,因为根据 Android 团队的说法,它会比直接访问 MediaStore 慢 2 到 3 倍。

【讨论】:

这似乎是这里提到的最糟糕的解决方案,因为它会阻止人们使用 Android 10 及更高版本安装应用程序 感谢您的反馈,我很惊讶地看到,developer.android.com/guide/topics/manifest/… 下的文档说无法安装,如果系统更新后重新验证,应用程序甚至会被删除 @Christian 好的,现在我明白你的困惑了。您提到的链接指的是“”元素,而不是。这是 2 种不同类型的元素,“maxSdkVersion”实际上是任何类型元素都存在的常见“属性”。您必须“不做”的是在“”元素中声明“maxSdkVersion”,但这与权限无关。查看实际的 文档,其中解释了如何使用“maxSdkVersion”来根据 API 级别限制权限:developer.android.com/guide/topics/manifest/… 好的,现在我明白你的想法了。但是您仍然无法在 Android 10 及更高版本上获得旧文件访问权限,对吗?像 folder.listFiles() 一样不起作用 @Christian 对于 Andorid 10 使用 requestLegacyExternalStorage。对于 Android 11,文件 API 可以“仅”在访问公共“共享存储”文件夹(DCIM、音乐等)或您的“私有”应用程序目录时使用。对于其他位置,请使用 DocumentFile API。请注意,API 30 中的文件 API 较慢,因为已将其重构为包装器以强制其仅在允许的位置使用。在内部将工作委托给 MediaStore。 “requestLegacyExternalStorage”标志在 Android 11 中不再有效,因为它只是暂时的,可以让开发人员有足够的时间来执行迁移。【参考方案3】:

正如@PerracoLabs 的回答,警告只是信息性的。如果一开始你会阅读他的答案会更好。 https://***.com/a/65477206/6055194

我使用WRITE_EXTERNAL_STORAGE 权限仅用于使用DownloadManager 将.pdf 文件下载到Downloads 和应用程序“私人”目录(这很重要,您可以理解为什么如果您将阅读@PerracoLabs 答案下的讨论)。所以我刚刚为这个权限添加了***别的 SDK,并忽略了作用域存储的标签。

<uses-permission
    android:name="android.permission.WRITE_EXTERNAL_STORAGE"
    android:maxSdkVersion="28"
    tools:ignore="ScopedStorage" />

附:如果无法识别tools 标签,则必须像这样将xmlns:tools="http://schemas.android.com/tools" 添加到您的AndroidManifest.xml 中来声明它。

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    package="com.app.my">

但是你必须注意,在它之后onRequestPermissionsResult 函数将不会被Build.VERSION_CODES.Q 和更高版本调用(API 29+)。 所以你必须调用请求权限WRITE_EXTERNAL_STORAGE只有API低于29。

所以你可以像这样(Kotlin)添加检查写入外部存储功能。

   private fun hasWriteStoragePermission(): Boolean 
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) 
            return true
         else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) 
            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
    

【讨论】:

【参考方案4】:

添加行:

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" tools:remove="android:maxSdkVersion"/>

并将以下代码添加到AndroidManifest.xml:

 android:requestLegacyExternalStorage="true"

【讨论】:

【参考方案5】:

使用 File-apis 吗?

根据@PerracoLabs 的回复,以下更改将使您的应用在运行的设备上运行

    Android-9 及更低版本 Android-10 Android-11 及以上版本

文件:“AndroidManifest.xml”

    //Without this folders will be inaccessible in Android-11 and above devices
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />

 //Without this entry storage-permission entry will not be visible under app-info permissions list Android-10 and below
<uses-permission
android:name="android.permission.WRITE_EXTERNAL_STORAGE"
android:maxSdkVersion="29"
tools:ignore="ScopedStorage"/>

    //Without this entry the folders will remain in-accessible in Android-10, even if WRITE_EXTERNAL_STORAGE as above is present.
<application
    android:requestLegacyExternalStorage="true"/>

Java 源码:

    在进行文件操作之前不要忘记给予运行时权限。 - Android-10 及以下版本需要。不要为 Android-11 请求运行时权限,Android-10 及更低版本需要请求。

    将用户导航到应用权限页面,以允许他使用以下代码启用文件权限(Android-11 及更高版本支持所需):

     if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R && false == Environment.isExternalStorageManager()) 
         Uri uri = Uri.parse("package:" + BuildConfig.APPLICATION_ID);
         startActivity(new Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION, uri));
     
    

【讨论】:

以上是关于针对 Android 10 时的 WRITE_EXTERNAL_STORAGE的主要内容,如果未能解决你的问题,请参考以下文章

Android10调用系统剪裁时的一个小问题

Android10调用系统剪裁时的一个小问题

运行 googlecast 示例时的 Android ClassNotFoundException

我可以针对较新的 android SDK 构建而不排除旧手机的所有者吗?

是否有针对 Android <10 上的任务劫持的修复?

Android模拟机访问本机时的ip地址设置问题