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

这是关于在请求Manifest.permission.WRITE_EXTERNAL_STORAGE权限时在Android Marshmallow中引入的新运行时权限模型。

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

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

我的应用程序从以下状态开始:

 ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED 

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

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

 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

 @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块,确认用户已授予权限。

在此之后,如果我不再销毁并再次打开应用程序,我仍然没有外部存储的访问权限。 进一步来说:

 hasWriteExternalStoragePermission(); // returns true Environment.getExternalStorageDirectory().canRead(); // RETURNS FALSE!! Environment.getExternalStorageDirectory().canWrite(); // RETURNS FALSE!! 

所以,似乎Android运行时认为我有权限,但文件系统没有…确实,尝试访问Environment.getExternalStorageDirectory()会抛出exception:

 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.(FileOutputStream.java:87) at java.io.FileOutputStream.(FileOutputStream.java:72) 

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

有人经历过这个吗?

我正在使用一个官方模拟器:

  • 最新的Android 6.0(API 23)API 23,Rev 1。
  • 仿真器运行Intel x86 Atom System Image,API 23,Rev 1。

我使用以下内容构建应用:

 android { compileSdkVersion 23 buildToolsVersion "22.0.1" defaultConfig { minSdkVersion 16 targetSdkVersion 23 } ... } 

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

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

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

“运行时权限请求”从API级别23开始,显然高于19,因此不再需要权限,除非您正在访问getExternalFilesDir()指向的文件夹之外的数据。 所以我相信这是模拟器的一个bug。

在级别19以下的较低目标(不支持在运行时请求权限)上,只需清除manifest中的权限,它就可以工作。

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

长话短说,我担心这不是模拟器中的错误,但实际上是Linux和Android的一个更大的问题。

我通过在第一次执行应用程序时请求权限来“解决”这个问题,并在给出权限时重新启动它,如下所示:

 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 )find了相关gid的列表。 更多信息可以在这里find: https : //android.googlesource.com/platform/frameworks/base/+/master/data/etc/platform.xml