奥利奥(WRITE EXTERNAL STORAGE)权限

23
根据developer.android.com网站的内容:
If the app targets Android 8.0 (API level 26), the system grants only 
READ_EXTERNAL_STORAGE at that time; however, if the app later requests 
WRITE_EXTERNAL_STORAGE, the system immediately grants that privilege 
without prompting the user.

现在,我拥有READ_EXTERNAL_STORAGE权限并请求DownloadManager下载一个文件到公共下载文件夹。
downloadRequest.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, mAttachmentItem.getFilename());

不幸的是,我得到了

E/UncaughtException: java.lang.IllegalStateException: Unable to create directory: /storage/emulated/0/data/user/0/com.abc.cba/files
                                                                           at android.app.DownloadManager$Request.setDestinationInExternalPublicDir(DownloadManager.java:699)

我已在清单文件中声明了这两个权限

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

已经授予读取权限,运行时正在请求写入权限,系统不会提示任何消息或对话框,在“onRequestPermissionsResult”中始终拒绝(未授予)。

public static boolean isPermissionsToAccessStorageAreGranted(Context context, Activity activity) {
    ArrayList<String> permissions = new ArrayList<>();

    if (!PermissionUtil.checkPermissions(context, Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
        permissions.add(Manifest.permission.WRITE_EXTERNAL_STORAGE);
    }

    if (!permissions.isEmpty()) {
        String[] permissionsList = permissions.toArray(new String[permissions.size()]);
        PermissionUtil.requestPermissions(activity, permissionsList,
                PermissionUtil.REQUESTCODE_ACCESS_STORAGE);
        return false;
    } else {
        return true;
    }
}


@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    if(requestCode==REQUESTCODE_ACCESS_STORAGE && grantResults[0]== PackageManager.PERMISSION_GRANTED){
 /* downloadFile(); */       
    }
}

我正在尝试通过ADB授予这两个权限,但出现错误:

adb shell pm grant com.abc.cba 
android.permission.WRITE_EXTERNAL_STORAGE
Operation not allowed: java.lang.SecurityException: Package com.abc.cbs 
has not requested permission android.permission.WRITE_EXTERNAL_STORAGE

adb shell pm grant com.abc.cba 
android.permission.READ_EXTERNAL_STORAGE
Operation not allowed: java.lang.SecurityException: Can't change 
android.permission.READ_EXTERNAL_STORAGE. It is required by the 
application

1
您需要在运行时请求WRITE_EXTERNAL_STORAGE权限。 - CommonsWare
我正在运行时请求WRITE_EXTERNAL_STORAGE权限,系统没有提示任何消息或对话框,在“onRequestPermissionsResult”中始终被拒绝(未授权)。 - SoulaimenK
它们位于正确的位置。 - SoulaimenK
/storage/emulated/0/data/user/0/com.abc.cba/files 这是一个非常奇怪的路径,它并不存在。请不要告诉我它等同于 Environment.DIRECTORY_DOWNLOADS, mAttachmentItem.getFilename() - greenapps
1
@svkaka:你尝试在清单文件(uses-permission中)设置tools:node="replace"了吗? - SoulaimenK
显示剩余4条评论
7个回答

31

实际上,这是一个外部问题。我正在使用的一个应用程序库请求android:maxSdkVersion下的WRITE_EXTERNAL_PERMISSION。因此,在与我的清单合并时,它将删除整个应用程序的权限。

解决方案是添加:

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" tools:node="replace"/> 

13

您在将权限添加到清单中是正确的:

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

对于Lollipop版本及以上的Android系统,您还需要在运行时请求权限。为了解决这个问题,我创建了一个新的方法requestAppPermission,当主Activity创建时调用此方法。该方法仅在Lollipop及以上版本上运行,否则会提前返回:

private void requestAppPermissions() {
    if (android.os.Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
        return;
    }

    if (hasReadPermissions() && hasWritePermissions()) {
        return;
    }

    ActivityCompat.requestPermissions(this,
            new String[] {
                    Manifest.permission.READ_EXTERNAL_STORAGE,
                    Manifest.permission.WRITE_EXTERNAL_STORAGE
            }, REQUEST_WRITE_STORAGE_REQUEST_CODE); // your request code
}

private boolean hasReadPermissions() {
    return (ContextCompat.checkSelfPermission(getBaseContext(), Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED);
}

private boolean hasWritePermissions() {
    return (ContextCompat.checkSelfPermission(getBaseContext(), Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED);
}

我在活动的onCreate中调用了这个方法:

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    // Other things
    requestAppPermissions();
}

首次加载时,这将提示用户接受权限。

然后,在运行需要读写存储的任何代码之前,您可以通过存储值或再次使用上面定义的hasReadPermissions()hasWritePermissions()方法运行这些检查来检查这些权限。


不对。运行时权限模型是在Marshmallow(API 23)中引入的,而不是在Lollipop中。对于Lollipop及更高版本,您仍需在运行时请求权限。 - Shashanth

6

更新:请参考此答案获得更好的解决方案。


编辑:这不是一个解决方案,只是一个快速的解决方法。

如果您已经授予权限并实施了权限检查,但仍然收到权限被拒绝的错误消息,

请确保您没有针对API级别29进行目标定位:

targetSdkVersion compilesdkversion29更改为28或任何其他较低级别。


7
这怎么能算是一个解决方案呢 - 不针对最新的API进行目标设置?请考虑建议如何在API 29中正确实施此操作。 - hb0

5

Marshmallow 或更高版本上,您需要在运行时授予权限。

示例代码:

private static final int REQUEST_WRITE_STORAGE = 112;

if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            boolean hasPermission = (ContextCompat.checkSelfPermission(getBaseContext(),
                    Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED);
            if (!hasPermission) {
                ActivityCompat.requestPermissions(SplashScreen.this,
                        new String[]{Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE,
                                Manifest.permission.ACCESS_NETWORK_STATE, Manifest.permission.RECORD_AUDIO, Manifest.permission.MODIFY_AUDIO_SETTINGS, Manifest.permission.INTERNET
                        },
                        REQUEST_WRITE_STORAGE);
            }else{ startMainActivity(); }
        }

希望这有所帮助。

5

在 Oreo 版本中,即使您已经请求并获得了 WRITE_EXTERNAL_STORAGE 权限,仍需要通过代码明确地进行 READ_EXTERNAL_STORAGE 请求,反之亦然。

在此之前,当 WRITE_EXTERNAL_STORAGE 被授予权限时,READ_EXTERNAL_STORAGE 会自动被授予。

因此,您需要执行以下操作:

1)在您的代码中请求 WRITE 权限。

2)当用户授予 WRITE 权限时,请再次请求 READ 权限-这将自动授予(如果您没有明确要求 READ,则会收到异常)

Oreo 的更改对我来说没有太多意义(不知道为什么需要请求自动授予的权限),但事实就是如此。

https://developer.android.com/about/versions/oreo/android-8.0-changes.html#rmp


1
当我首先请求WRITE时,我总是得到-1。 - svkaka
2
@svkaka,我认为一般的想法是只有在需要时才请求权限(例如,当用户点击“拍照”按钮时提示相机/写入权限),而不是在用户打开应用程序时立即请求。 - Angel Koh

3

请使用以下代码:

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

代替以下代码:

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

一些第三方库会覆盖maxSdkVersion,例如com.vungle:publisher-sdk-android。您可以在清单屏幕下方的合并清单中查找它们。请参见此处

针对SDK 29及更高版本

如果您仍然无法访问外部存储,请在清单文件的<application>标记中使用android:requestLegacyExternalStorage="true"


1
android:requestLegacyExternalStorage="true" 这个在安卓9中解决了我的问题,谢谢。 - Bikesh M

2

提醒一下:在Android P上,我不得不向用户显示一个终止对话框,要求重新启动应用程序,因为即使按照上述方法,应用程序仍无法读取外部存储器,只有重新启动后才能读取。谷歌的这些家伙每次都会让Android变得更好。6、8和现在9都有很多麻烦,如果再出现这样愚蠢的更新,我就去iOS了:)


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接