API 17(4.2)中授予权限的EACCES(权限被拒绝)

时间:2017-03-14 12:02:27

标签: android permissions

我有一个正确使用"外部存储"中的文件的应用程序。 最近我将Android Studio从2.2升级到2.3。在此升级之后,使用EACCESS (Permission denied)在外部存储中创建文件时,应用程序将失败。

受影响的版本

我在 Android 4.0.3,4.1,4.2

中有错误

我在 Android 4.3,4.4 及更高版本中没有错误。

代码示例

这是代码的一部分,失败

File tempFile = new File(Environment.getExternalStorageDirectory().toString() + "/.tmpfile");

if (!tempFile.exists() && !tempFile.createNewFile()) {
    throw new IOException("Cannot create temp file");
}

在方法createNewFile()中,EACCES抛出异常:

java.io.IOException: open failed: EACCES (Permission denied)
      at java.io.File.createNewFile(File.java:948)
      ...
  Caused by: libcore.io.ErrnoException: open failed: EACCES (Permission denied)
      at libcore.io.Posix.open(Native Method)
      at libcore.io.BlockGuardOs.open(BlockGuardOs.java:110)
      at java.io.File.createNewFile(File.java:941)
      ...
  java.io.IOException: open failed: EACCES (Permission denied)
      at java.io.File.createNewFile(File.java:948)
      ...
  Caused by: libcore.io.ErrnoException: open failed: EACCES (Permission denied)
      at libcore.io.Posix.open(Native Method)
      at libcore.io.BlockGuardOs.open(BlockGuardOs.java:110)
      at java.io.File.createNewFile(File.java:941)
    ... 15 more

当然,<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />中有AndroidManifest.xml。 应用程序已授予权限。我通过以下方式在主要活动中查看:

if (checkCallingOrSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
    ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, WRITE_EXTERNAL_STORAGE_CODE);
}

并且requestCode == PackageManager.PERMISSION_GRANTED是真的。

最奇怪的:当我尝试将代码回滚到之前稳定的生产版本时(当文件在2.2中成功创建时)并创建构建版本时,我得到相同的内容错误! :(我在模拟器中,在真实设备中得到此错误。

为什么呢? Android Studio 2.3中有哪些更改,我无法在旧Android中创建任何文件?

UPD 感谢尼克,他帮助我找到行为上的差异。我在Android 4.2和4.3上测试下面的行,然后得到:

Environment.getExternalStorageDirectory()
4.2: '/mnt/sdcard'
4.3: '/storage/sdcard'

ContextCompat.getExternalFilesDirs(this, null)
4.2: {null} (array with one null-element)
4.3: '/storage/sdcard/Android/data/com.example/files'

Environment.getExternalStorageState()
4.2: 'removed'
4.3: 'mounted'

1 个答案:

答案 0 :(得分:3)

您的问题与Android 4.4中所做的更改有关。引用documentation

  

有时,设备也可能提供SD卡插槽。当这样的设备运行Android 4.3及更低版本时,getExternalFilesDir()方法仅提供对内部分区的访问,并且您的应用无法读取或写入SD卡

该文档还提供了解决方案:

  

如果您想在支持Android 4.3及更低版本的同时访问这两个可能的位置,请使用支持库的静态方法

这指的是使用ContextCompat而非核心Android方法

File[] dirs = ContextCompat.getExternalFilesDirs(ctx, null); //null, no specific sub directory
if (dirs.length > 0) {
    File ext = dirs[dirs.length -1]; //Just presuming SD card will be the last one offered
    //use ext here like you used tempFile before
}

您还应该确保媒体可用,因为较低的Android版本更有可能使用低性能设备。使用Environment类

执行此操作
String state = Environment.getExternalStorageState();
if (Environment.MEDIA_MOUNTED.equals(state)) {
     //safe
}

编辑

因此,完整的解决方案可能是替换

File tempFile = new File(Environment.getExternalStorageDirectory().toString() + "/.tmpfile");

使用

File tempFile = null;
String state = Environment.getExternalStorageState();
if (Environment.MEDIA_MOUNTED.equals(state)) {
    //compatible for ALL the versions
    File[] dirs = ContextCompat.getExternalFilesDirs(ctx, null); //null, no specific sub directory
    if (dirs.length > 0) {
        tempFile  = dirs[dirs.length -1]; 
    }
}
if (tempFile != null) {
    //here continue exactly as you did before
    if (!tempFile.exists() && !tempFile.createNewFile()) {
        throw new IOException("Cannot create temp file");
    }
} else {
    //handle case where sd card isnt reachable (notification etc)
}