问题摘要:我的安卓应用程序尝试将写入SD卡上应用程序的外部存储目录。它会失败,并显示权限错误。但同样的代码(方法),提取到一个最小的测试应用程序中,就成功了!
由于我们的目标应用程序接口级别包括KitKat和更高版本(以及JellyBean),并且KitKat限制应用程序在SD卡上除指定的外部存储目录之外的任何位置写入,因此应用程序会尝试写入指定的目录/path/to/sdcard/Android/data/com.example.myapp/files
。我通过从Activity.getExternalFilesDirs(null);
获取一个目录列表并找到一个isRemovable()
来验证这个目录的路径。也就是说,我们没有硬编码到SD卡的路径,因为它因制造商和设备而异。以下是演示该问题的代码:
// 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()方法并不重要,但为了完整起见,我将在这里介绍它。它只是确保目录位于已挂载的可移动存储上,并记录该目录的其他属性(存在、可写)。
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上)中,以下日志输出显示代码运行正常:
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()
的调用失败,并出现权限错误:
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卡时的权限故障。但是,给出的原因或解决方案似乎都不适用于这种情况:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
targetSdkVersion 22
(还有buildToolsVersion '21.1.2'
、KitKat和棒棒糖),我甚至没有棉花糖设备。同样,我不应该在运行时请求权限,即使我的目标是API级别23。< code >F245
何时有效,何时无效的摘要:
测试应用程序和真正的应用程序之间有什么区别?很明显,真正的应用程序中有更多的东西。但我看不出有什么重要的。两者都有相同的compileSdkVersion
、targetSdkVersion
、buildToolsVersion
等。它们也都使用compile 'com.android.support:appcompat-v7:23.4.0'
作为依赖项。
发布于 2017-04-29 00:58:26
我对这个问题有了更多的了解,它与CommonsWare的答案有很大的不同,我认为它值得一个新的答案。
/Android/data/com.example.myapp
文件夹,而这个文件夹不是在这部手机上创建的,那么应用程序可能无法获得写入该文件夹的权限。然而,如果文件夹不存在,应用程序可以创建它,并将其写入。至少在KitKat和更高版本中是这样。- 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上进行测试。)发布于 2016-10-29 04:37:38
由于一个应用程序可以工作,而另一个应用程序不能工作,所以区别在于应用程序之间,而不是设备或卡。有问题的目录不需要任何Android权限(例如WRITE_EXTERNAL_STORAGE
)。无法写入的唯一原因是Android系统没有正确设置文件系统权限。
我可能自己创建了该目录,但在某个地方设置权限失败?
我不确定你能不能从应用程序之外自己创建那个目录并让它工作。理想情况下,这很好,但我没有尝试过,我可以看到可能会带来问题的地方。
还有其他不信任它的理由吗?
考虑到正在进行的文件系统恶作剧,当开发人员对路径的性质进行假设时,我会非常紧张,仅此而已。
https://stackoverflow.com/questions/40252541
复制相似问题