Lar*_*rsH 19 android file-permissions android-sdcard android-external-storage
TL; DR问题摘要:我的Android应用程序尝试写入 SD卡上应用程序的外部存储目录.它失败并出现权限错误.但是,提取到最小测试应用程序中的相同代码(方法)成功了!
由于我们的目标API级别包括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);
}
}
Run Code Online (Sandbox Code Playgroud)
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)));
}
Run Code Online (Sandbox Code Playgroud)
在测试应用程序(在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
Run Code Online (Sandbox Code Playgroud)
因此文件已成功创建.但在我的实际应用程序中(也在Android 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)
...
Run Code Online (Sandbox Code Playgroud)
在将此标记为重复之前:我已经阅读了几个关于SO的其他问题,这些问题描述了在KitKat或更高版本下写入SD卡时的权限失败.但是,给出的原因或解决方案似乎都不适用于这种情况:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />targetSdkVersion 22(和buildToolsVersion '21.1.2',compileSdkVersion 23).它何时起作用以及何时起作用的摘要:
测试应用程序和真实应用程序之间有什么区别?嗯,显然真正的应用程序中有更多的东西.但我看不出任何重要的事情.两者具有相同的compileSdkVersion,targetSdkVersion,buildToolsVersion等两者也使用compile 'com.android.support:appcompat-v7:23.4.0'作为依赖.
由于一个应用程序正常工作而另一个应用程序没有,因此不同之处在于应用程序之间,而不是设备或卡.相关目录不需要任何Android权限(例如WRITE_EXTERNAL_STORAGE).如果Android系统没有正确设置文件系统权限,那么你无法写入它的唯一原因就是它.
我自己可能创建了该目录,但未能在某处设置权限?
我不确定您是否可以从应用程序外部创建该目录并使其正常工作.理想情况下这样会很好,但我没有尝试过,我可以看到可能造成问题的地方.
还有另一个不信任它的理由吗?
鉴于正在进行的文件系统恶作剧,当开发人员对路径的性质做出假设时,我会非常紧张,这就是全部.
我对这个问题有了更多的了解,它与 CommonsWare 的答案有很大不同,我认为值得一个新的答案。
\n\n/Android/data/com.example.myapp文件夹,那么应用程序可能无法获得写入该文件夹的权限。然而,如果该文件夹不存在,应用程序可以创建它并写入它。至少在 KitKat 及更高版本中是这样。\n\n\n...从 API 级别 19 [KitKat] 开始,不再需要 READ_EXTERNAL_STORAGE 来访问位于外部存储 \xe2\x80\x93 上的文件,前提是FUSE 守护程序创建的数据文件夹与 app\xe2\x80\x99s 包名称匹配。当安装应用程序时, FUSE 将处理综合外部存储上文件的所有者、组和模式[强调]。
\n
context.getExternalFilesDirs(null)才能/Android/data/com.example.myapp在 SD 卡上创建文件夹。(至少,在 Android 5.1 Lollipop 及更高版本上。需要在 KitKat 上进行测试。)| 归档时间: |
|
| 查看次数: |
1955 次 |
| 最近记录: |