首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >为什么我没有权限写入外部存储上的app dir?

为什么我没有权限写入外部存储上的app dir?
EN

Stack Overflow用户
提问于 2016-10-26 10:01:57
回答 2查看 2.9K关注 0票数 19

问题摘要:我的安卓应用程序尝试将写入SD卡上应用程序的外部存储目录。它会失败,并显示权限错误。但同样的代码(方法),提取到一个最小的测试应用程序中,就成功了!

由于我们的目标应用程序接口级别包括KitKat和更高版本(以及JellyBean),并且KitKat限制应用程序在SD卡上除指定的外部存储目录之外的任何位置写入,因此应用程序会尝试写入指定的目录/path/to/sdcard/Android/data/com.example.myapp/files。我通过从Activity.getExternalFilesDirs(null);获取一个目录列表并找到一个isRemovable()来验证这个目录的路径。也就是说,我们没有硬编码到SD卡的路径,因为它因制造商和设备而异。以下是演示该问题的代码:

代码语言:javascript
复制
// Attempt to create a test file in dir.
private void testCreateFile(File dir) {
    Log.d(TAG, ">> Testing dir " + dir.getAbsolutePath());

    if (!checkDir(dir)) { return; }

    // Now actually try to create a file in this dir.
    File f = new File(dir, "foo.txt");
    try {
        boolean result = f.createNewFile();
        Log.d(TAG, String.format("Attempted to create file. No errors. Result: %b. Now exists: %b",
                result, f.exists()));
    } catch (Exception e) {
        Log.e(TAG, "Failed to create file " + f.getAbsolutePath(), e);
    }
}

checkDir()方法并不重要,但为了完整起见,我将在这里介绍它。它只是确保目录位于已挂载的可移动存储上,并记录该目录的其他属性(存在、可写)。

代码语言:javascript
复制
private boolean checkDir(File dir) {
    boolean isRemovable = false;
    // Can't tell whether it's removable storage?
    boolean cantTell = false;
    String storageState = null;

    // Is this the primary external storage directory?
    boolean isPrimary = false;
    try {
        isPrimary = dir.getCanonicalPath()
                .startsWith(Environment.getExternalStorageDirectory().getCanonicalPath());
    } catch (IOException e) {
        isPrimary = dir.getAbsolutePath()
                .startsWith(Environment.getExternalStorageDirectory().getAbsolutePath());
    }

    if (isPrimary) {
        isRemovable = Environment.isExternalStorageRemovable();
        storageState = Environment.getExternalStorageState();
    } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
        // I actually use a try/catch for IllegalArgumentException here, but 
        // that doesn't affect this example.
        isRemovable = Environment.isExternalStorageRemovable(dir);
        storageState = Environment.getExternalStorageState(dir);
    } else {
        cantTell = true;
    }

    if (cantTell) {
        Log.d(TAG, String.format("  exists: %b  readable: %b  writeable: %b primary: %b  cantTell: %b",
                dir.exists(), dir.canRead(), dir.canWrite(), isPrimary, cantTell));
    } else {
        Log.d(TAG, String.format("  exists: %b  readable: %b  writeable: %b primary: %b  removable: %b  state: %s  cantTell: %b",
                dir.exists(), dir.canRead(), dir.canWrite(), isPrimary, isRemovable, storageState, cantTell));
    }

    return (cantTell || (isRemovable && storageState.equalsIgnoreCase(MEDIA_MOUNTED)));
}

在测试应用(运行在Android 5.1.1上)中,以下日志输出显示代码运行正常:

代码语言:javascript
复制
10-25 19:56:40 D/MainActivity: >> Testing dir /storage/extSdCard/Android/data/com.example.testapp/files
10-25 19:56:40 D/MainActivity:   exists: true  readable: true  writeable: true primary: false  removable: true  state: mounted  cantTell: false
10-25 19:56:40 D/MainActivity: Attempted to create file. No errors. Result: false. Now exists: true

因此,该文件已成功创建。但在我的实际应用中(也运行在安卓5.1.1上),对createNewFile()的调用失败,并出现权限错误:

代码语言:javascript
复制
10-25 18:14:56... D/LessonsDB: >> Testing dir /storage/extSdCard/Android/data/com.example.myapp/files
10-25 18:14:56... D/LessonsDB:   exists: true  readable: true  writeable: true primary: false  removable: true  state: mounted  cantTell: false
10-25 18:14:56... E/LessonsDB: Failed to create file /storage/extSdCard/Android/data/com.example.myapp/files/foo.txt
    java.io.IOException: open failed: EACCES (Permission denied)
        at java.io.File.createNewFile(File.java:941)
        at com.example.myapp.dmm.LessonsDB.testCreateFile(LessonsDB.java:169)
    ...
     Caused by: android.system.ErrnoException: open failed: EACCES (Permission denied)
        at libcore.io.Posix.open(Native Method)
        at libcore.io.BlockGuardOs.open(BlockGuardOs.java:186)
        at java.io.File.createNewFile(File.java:934)
    ...

在您将此标记为副本之前:我已经通读了其他几个问题,描述了在KitKat或更高版本下写入SD卡时的权限故障。但是,给出的原因或解决方案似乎都不适用于这种情况:

  • The device is connected as 大容量存储。我仔细检查过了。但是,MTP处于打开状态。(如果不拔下用来查看日志的USB线,我就无法将其关闭。)
  • 我的清单包含了针对<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
  • I'm级别22的API,因此我不应该在运行时(就像Marshmallow).一样)请求权限build.gradle有targetSdkVersion 22 (还有buildToolsVersion '21.1.2'、KitKat和棒棒糖),我甚至没有棉花糖设备。同样,我不应该在运行时请求权限,即使我的目标是API级别23。
  • 如上所述,我正在写入指定的外部存储目录,该目录应该可以由我的应用程序写入,即使在KitKat上也是如此。
  • 外部存储是装载的;代码将验证这一点。

< code >F245

何时有效,何时无效的摘要:

  • 在我的pre-KitKat设备上,测试应用程序和真正的应用程序都可以正常工作。他们成功地在SD卡上的应用程序目录中创建了一个文件。
  • 在我的KitKat和棒棒糖设备上,测试应用程序运行正常,但真正的应用程序却无法正常工作。相反,它会在上面的日志中显示错误。

测试应用程序和真正的应用程序之间有什么区别?很明显,真正的应用程序中有更多的东西。但我看不出有什么重要的。两者都有相同的compileSdkVersiontargetSdkVersionbuildToolsVersion等。它们也都使用compile 'com.android.support:appcompat-v7:23.4.0'作为依赖项。

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2017-04-29 00:58:26

我对这个问题有了更多的了解,它与CommonsWare的答案有很大的不同,我认为它值得一个新的答案。

  • 让我的原始场景与众不同的是SD卡:如果一张卡上已经有一个/Android/data/com.example.myapp文件夹,而这个文件夹不是在这部手机上创建的,那么应用程序可能无法获得写入该文件夹的权限。然而,如果文件夹不存在,应用程序可以创建它,并将其写入。至少在KitKat和更高版本中是这样。
    • 这是由this article中解释的内容支持的:...从API19级KitKat开始,READ_EXTERNAL_STORAGE不再需要访问位于外部存储上的文件-前提是由FUSE守护程序创建的数据文件夹与应用程序的包名相匹配。FUSE将在安装应用程序时处理合成外部存储上的文件的所有者、组和模式。强调FUSEFUSE

代码语言:javascript
复制
- So this confirms the guess that the problem arose because I created the app's data folder manually, instead of letting Android set it up as it needed. Future research: Instead of looking only to app permissions like WRITE\_EXTERNAL\_STORAGE, also check the user, group, and mode settings of the app's data folder on the filesystem: both when created manually, and when created by app installation. Compare & contrast! Maybe this will provide enough information to allow the app's data folder to be created manually and still work. Keep in mind that there is a wrapper / emulation layer over the FAT32 filesystem of the SD card, so we need to consider both filesystem layers.

  • 在后面的场景中,我发现应用程序必须调用context.getExternalFilesDirs(null)才能在SD卡上创建/Android/data/com.example.myapp文件夹。(至少,在Android 5.1棒棒糖和更高版本上。需要在KitKat上进行测试。)
票数 2
EN

Stack Overflow用户

发布于 2016-10-29 04:37:38

由于一个应用程序可以工作,而另一个应用程序不能工作,所以区别在于应用程序之间,而不是设备或卡。有问题的目录不需要任何Android权限(例如WRITE_EXTERNAL_STORAGE)。无法写入的唯一原因是Android系统没有正确设置文件系统权限。

我可能自己创建了该目录,但在某个地方设置权限失败?

我不确定你能不能从应用程序之外自己创建那个目录并让它工作。理想情况下,这很好,但我没有尝试过,我可以看到可能会带来问题的地方。

还有其他不信任它的理由吗?

考虑到正在进行的文件系统恶作剧,当开发人员对路径的性质进行假设时,我会非常紧张,仅此而已。

票数 8
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/40252541

复制
相关文章

相似问题

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