针对 Android 10 时的 WRITE_EXTERNAL_STORAGE

Mih*_*eel 62 android android-permissions

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

Per*_*abs 41

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

<uses-permission
    android:name="android.permission.WRITE_EXTERNAL_STORAGE"
    android:maxSdkVersion="28"
    tools:ignore="ScopedStorage" />
Run Code Online (Sandbox Code Playgroud)

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

更新,澄清一些开发者在评论中的疑惑和困惑:

如果在Android 10 中使用requestLegacyExternalStorage标志,则像往常一样请求 WRITE_EXTERNAL_STORAGE 权限。

标志requestLegacyExternalStorage在 Android 11 中没有任何作用,它被完全忽略。

WRITE_EXTERNAL_STORAGE 在 Android 11 中不授予任何权限,它什么也不做,因此您需要将maxSdkVersion设置为 29。

如果在 Android 10 中也不使用requestLegacyExternalStorage,则将 maxSdkVersion 设置为 28 而不是 29。

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

考虑到文件 API 现在在 API 30 中要慢得多,因为已经重构为本质上是一个包装器,这是为了仅将其使用强制到允许的位置。So 不再是一个快速的系统文件 API,只是一个在内部将工作委托给 MediaStore 的包装器。在 Android 11 或更高版本中使用 File API 时,您应该考虑性能损失。

  • 这似乎是这里提到的最糟糕的解决方案,因为它会阻止人们使用 Android 10 及更高版本安装该应用程序 (10认同)
  • @Christian 好吧,现在我明白你的困惑了。您提到的链接是指“&lt;uses-sdk&gt;”元素,而不是&lt;uses-permission&gt;。这是两种不同类型的元素,“maxSdkVersion”实际上是任何类型元素都存在的通用“属性”。你必须“不做”的是在“&lt;uses-sdk&gt;”元素中声明“maxSdkVersion”,但这与权限无关。检查实际的 &lt;uses-permision&gt; 文档,其中解释了如何使用“maxSdkVersion”根据 API 级别限制权限:https://developer.android.com/guide/topics/manifest/uses-permission-element (3认同)

小智 30

使用文件 API ?

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

  1. Android-9 及更低版本
  2. Android-10
  3. 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"/>
Run Code Online (Sandbox Code Playgroud)

Java来源:

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

  2. 将用户导航到应用程序权限页面,以允许他使用以下代码启用文件权限(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));
     }
    
    Run Code Online (Sandbox Code Playgroud)

  • “Android-9 及以下 Android-10 Android-11 及以上”是我见过的表达“所有 Android 版本”的最复杂的方式。 (4认同)

Fra*_*cci 24

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

<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>
Run Code Online (Sandbox Code Playgroud)

READ_EXTERNAL_STORAGE再次删除权限:

<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
Run Code Online (Sandbox Code Playgroud)

  • 注意:将应用更新为面向 Android 11(API 级别 30)后,当您的应用在 Android 11 设备上运行时,系统会忽略 requestLegacyExternalStorage 属性,因此您的应用必须准备好支持范围存储并迁移应用数据这些设备上的用户。` 另请参阅 /sf/ask/4435513351/ (10认同)
  • 不再需要 `requestLegacyExternalStorage` 并且对于 Android 10 来说不是一个好的解决方案,因为如果您想将文件存储在 `Internal Storage/Pictures/MY_APP_NAME` 中,可以使用 MediaStore、ContentResolver 和 ContentValues 等 API (4认同)
  • 不,这是一个非常好的解决方案。您可以继续使用经典文件路径。在 Android 11 上,您也可以仅使用经典文件路径写入图片文件夹。不需要 mediastore 和 saf (3认同)
  • 是的,旧请求对 Android 11 设备没有任何作用。但它仍然适用于 Android 10 设备。所以我想说的是继续使用它,这就是我所说的。 (3认同)
  • @blackapps 请阅读 https://developer.android.com/training/data-storage/use-cases (2认同)
  • 再说一次:在 Android 11 设备上,您的应用程序可以直接写入 /storage/emulated/0/Pictures 本身。不需要媒体存储和安全。正如 @Siddharth Kamaria 所建议的那样,您不需要 MANAGE_EXTERNAL_STORAGE 。 (2认同)

Zhe*_*ich 16

正如@PerracoLabs 所回答的,警告只是提供信息。如果你一开始就阅读他的答案会更好。/sf/answers/4583404451/

我仅使用权限WRITE_EXTERNAL_STORAGE.pdf 文件下载到Downloads应用程序“私有”目录DownloadManager中(这很重要,如果您阅读 @PerracoLabs 答案下的讨论,您就会明白为什么)。因此,我刚刚为此权限添加了 SDK 的最大级别,并忽略了范围存储的标记。

<uses-permission
    android:name="android.permission.WRITE_EXTERNAL_STORAGE"
    android:maxSdkVersion="28"
    tools:ignore="ScopedStorage" />
Run Code Online (Sandbox Code Playgroud)

PS 如果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">
Run Code Online (Sandbox Code Playgroud)

但必须注意,此后的onRequestPermissionsResult函数将不会被调用Build.VERSION_CODES.Q以及更高版本(API 29+)。所以必须WRITE_EXTERNAL_STORAGE只对低于29的API调用请求权限。

所以你可以像这样添加检查写入外部存储功能(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
    }
Run Code Online (Sandbox Code Playgroud)