首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >在运行时请求和允许WRITE_EXTERNAL_STORAGE权限对当前会话没有影响

在运行时请求和允许WRITE_EXTERNAL_STORAGE权限对当前会话没有影响
EN

Stack Overflow用户
提问于 2015-10-05 19:35:38
回答 3查看 31.6K关注 0票数 25

这是关于请求Manifest.permission.WRITE_EXTERNAL_STORAGE权限时的新型号的runtime permissions introduced in Android Marshmallow

简而言之,我遇到的情况是,如果我请求(并且用户允许) Manifest.permission.WRITE_EXTERNAL_STORAGE权限,应用程序将无法从外部存储目录中读取和写入,直到我销毁并重新启动应用程序。

这就是我正在做的/正在经历的:

我的应用程序从一种状态开始:

代码语言:javascript
复制
ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED

这就是说,我没有访问外部存储的权限。

然后,我请求Manifest.permission.WRITE_EXTERNAL_STORAGE的许可,就像谷歌解释的那样

代码语言:javascript
复制
private void requestWriteExternalStoragePermission() {
    // Should we show an explanation?
    if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
        new AlertDialog.Builder(this)
                .setTitle("Inform and request")
                .setMessage("You need to enable permissions, bla bla bla")
                .setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        ActivityCompat.requestPermissions(MendeleyActivity.this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, RC_PERMISSION_WRITE_EXTERNAL_STORAGE);
                    }
                })
                .show();
    } else {
        ActivityCompat.requestPermissions(MendeleyActivity.this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, RC_PERMISSION_WRITE_EXTERNAL_STORAGE);
    }
}

一旦用户允许该权限,就会调用onRequestPermissionsResult

代码语言:javascript
复制
@Override
public void onRequestPermissionsResult(int requestCode,  String permissions[], int[] grantResults) {
    switch (requestCode) {
        case RC_PERMISSION_WRITE_EXTERNAL_STORAGE: {
            // If request is cancelled, the result arrays are empty.
            if (grantResults.length > 0 && PackageManager.PERMISSION_GRANTED
                // allowed 
            } else {
                // denied
            }
            break;
        }
    }
}

执行allowed块,确认用户已被授予权限。

在此之后立即,如果我不销毁并再次打开应用程序,我仍然没有访问外部存储的权限。更确切地说:

代码语言:javascript
复制
hasWriteExternalStoragePermission();                  // returns true
Environment.getExternalStorageDirectory().canRead();  // RETURNS FALSE!!
Environment.getExternalStorageDirectory().canWrite(); // RETURNS FALSE!!

所以,看起来Android运行时认为我有权限,但文件系统没有...实际上,尝试访问Environment.getExternalStorageDirectory()会抛出一个异常:

代码语言:javascript
复制
android.system.ErrnoException: open failed: EACCES (Permission denied)
at libcore.io.Posix.open(Native Method)
at libcore.io.BlockGuardOs.open(BlockGuardOs.java:186)
at libcore.io.IoBridge.open(IoBridge.java:438)
at java.io.FileOutputStream.<init>(FileOutputStream.java:87) 
at java.io.FileOutputStream.<init>(FileOutputStream.java:72) 

如果我现在销毁应用程序,并再次打开它的,行为变得像它应该,能够读取和写入外部存储文件夹。

有没有人遇到过这种情况?

我使用了一个官方的模拟器:

  • 最新的安卓6.0 (API 23) API 23,版本1。运行英特尔x86原子系统镜像的
  • 仿真器,API 23,版本1。

我用以下命令构建应用程序:

代码语言:javascript
复制
android {
    compileSdkVersion 23
    buildToolsVersion "22.0.1"

    defaultConfig {
        minSdkVersion 16
        targetSdkVersion 23
    }
...
}

如果有人确认了这一点,我想我们需要打开一个bug,但我希望我做错了什么,因为我认为这样的核心功能在SDK中不太可能有bug。

EN

回答 3

Stack Overflow用户

发布于 2015-10-22 12:02:55

http://developer.android.com/reference/android/Manifest.permission.html#WRITE_EXTERNAL_STORAGE

从API level 19开始,在由getExternalFilesDir(String)和getExternalCacheDir()返回的特定于应用程序的目录中读/写文件不需要此权限。

运行时权限开始于API23级,显然高于19级,因此不再需要该权限,除非您正在访问getExternalFilesDir()所指向的文件夹之外的数据。因此,我认为这是一个只有在模拟器上测试时才会出现的错误。

在19级以下的目标上,不支持在运行时请求权限,只需在清单中声明权限,它就会工作。

票数 8
EN

Stack Overflow用户

发布于 2016-05-10 17:56:19

我也有同样的问题。事实证明这似乎是一个更大的问题。更改写入外部存储的权限会更改此进程的GID (在linux端)。为了更改ID,必须重新启动该进程。下次打开应用程序时,将设置新的groupID并授予权限。

长话短说,恐怕这不是模拟器中的bug,而是Linux和Android的一个更大的问题。

我“解决”了这个问题,在应用程序第一次执行时请求许可,当许可如下时重新启动它:

代码语言:javascript
复制
PackageManager packageManager = getPackageManager();
                    Intent intent = packageManager.getLaunchIntentForPackage(getPackageName());
                    ComponentName componentName = intent.getComponent();
                    Intent mainIntent = IntentCompat.makeRestartActivityTask(componentName);
                    startActivity(mainIntent);
                    System.exit(0);

您可以尝试创建一个在后台运行的服务(具有另一个进程id),并为其提供权限。这样,你只需要重新启动服务,而不是整个应用程序。在不好的方面,这可能会给你带来更多的工作。

希望这能有所帮助。

编辑--

用户M66B (https://stackoverflow.com/a/32473449/1565635)找到了相关gids的列表。欲了解更多信息,请点击此处:https://android.googlesource.com/platform/frameworks/base/+/master/data/etc/platform.xml

票数 5
EN

Stack Overflow用户

发布于 2020-09-02 22:05:44

用于安卓10+的

在我的例子中,它是:

如果目标是Android 10 (API29级)或更高级别,请在应用程序的AndroidManifest文件中将requestLegacyExternalStorage的值设置为true。只需将以下代码添加到您的Application块中。

代码语言:javascript
复制
<application
        
        android:requestLegacyExternalStorage="true"
        ... >
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/32947638

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档