在我的应用中,用户选择文件。在内部,我存储有关文件的信息,我根据文件路径键入该文件。下次使用该文件时,我会处理存储的信息。麻烦的是我用以下方式实例化我的文件:
File file1 = new File(Environment.getExternalStorageDirectory() + "/test.txt");
然后,在特定的JB设备上,file1.getCanonicalPath()给出:“/ storage_emulated/0 / test.txt”。
问题在于,当其他应用程序在Intent中使用文件路径启动我的应用程序时,它们发送的路径往往看起来像:“/ mnt /sdcard / test.txt”。
是否有明智的策略消除这两条路径的歧义?可能我应该以不同方式实例化我的文件?
编辑:
麻烦的是,两个文件的两个canaonical路径不相等。对于以下内容,cp1=="mnt/sdcard/test/txt"
和cp2=="/storage/emulated/0/text/txt"
:
File file1 = new File("/mnt/sdcard/test.txt");
File file2 = new File("/storage/emulated/0/test.txt");
String cp1 = file1.getCanonicalPath();
String cp2 = file2.getCanonicalPath();
答案 0 :(得分:5)
首先,获取外部路径的唯一正确方法是使用Android中的getExternalStorageDirectory
和其他getExternalStorageXXX
。
Android将首先尝试解析两个系统变量:
String rawExternalStorage = System.getenv(ENV_EXTERNAL_STORAGE);
String rawEmulatedStorageTarget = System.getenv(ENV_EMULATED_STORAGE_TARGET);
ENV_EXTERNAL_STORAGE = "EXTERNAL_STORAGE"
和ENV_EMULATED_STORAGE_TARGET = "EMULATED_STORAGE_TARGET"
。如果设置了EMULATED_STORAGE_TARGET
变量,则表示设备已模拟存储,然后存储路径为EMULATED_STORAGE_TARGET
。(在Android 4.2之后,它支持多用户外部存储,那么将有一个/ 0或路径后面的0)但是如果未设置并设置EXTERNAL_STORAGE
,则路径将为EXTERNAL_STORAGE
。如果未设置它们,则默认情况下路径为/storage/sdcard0
。因此,不同的设备可能包含不同的外部存储路径。
如External Storage Technical Information所述,您可以通过设置init.rc文件来自定义设备的存储。例如在默认的金鱼中:
export EXTERNAL_STORAGE /mnt/sdcard
mkdir /mnt/sdcard 0000 system system
symlink /mnt/sdcard /sdcard
如果您使用getExternalStorageDirectory
,则会获得/mnt/sdcard
,但/sdcard
是指向该目录的符号链接。
因此,在您的情况下,init.rc可能包含:
export EMULATED_STORAGE_TARGET /storage/emulated
symlink /storage/emulated/0 /mnt/sdcard
所以他们并不含糊,他们实际上是一样的。
我认为getCanonicalPath()可能适用于绝大多数用例。
规范路径名既是绝对路径名也是唯一路径名。准确的 规范形式的定义是系统依赖的。这个方法首先 如有必要,将此路径名转换为绝对形式,就像使用 调用getAbsolutePath()方法,然后将其映射到其唯一 以系统相关的方式形成。这通常涉及删除 冗余名称,如“。”和路径名中的“..”,解决 符号链接(在UNIX平台上),并将驱动器号转换为 标准案例(在Microsoft Windows平台上)。
表示现有文件或目录的每个路径名都具有唯一性 规范形式。表示不存在的文件或的每个路径名 目录也有一个独特的规范形式。规范的形式 不存在的文件或目录的路径名可能与 文件或目录之后的相同路径名的规范形式 创建。类似地,现有路径名的规范形式 文件或目录可能与规范形式不同 删除文件或目录后的路径名。
答案 1 :(得分:2)
对此可能没有简单的解决方案。问题是显然文件系统中有两个不同的挂载点实际指向同一位置。 File.getCanonicalPath()只能解析符号链接,但不能解析不同的挂载点。
例如在我的Nexus 4上这段代码:
File file1 = new File(Environment.getExternalStorageDirectory() + "/Android");
System.out.println("file 1: " + file1.getCanonicalPath());
File file2 = new File("/sdcard/Android");
System.out.println("file 2: " + file2.getCanonicalPath());
打印
file 1: /storage/emulated/0/Android
file 2: /storage/emulated/legacy/Android
我使用此代码执行“mount”并打印输出:
Process exec = Runtime.getRuntime().exec("mount");
InputStream in = exec.getInputStream();
BufferedReader br = new BufferedReader(new InputStreamReader(in));
while (true) {
String line = br.readLine();
if (line == null)
break;
System.out.println(line);
}
in.close();
相关输出是:
/dev/fuse /storage/emulated/0 fuse rw,nosuid,nodev,relatime,user_id=1023,group_id=1023,default_permissions,allow_other 0 0
/dev/fuse /storage/emulated/legacy fuse rw,nosuid,nodev,relatime,user_id=1023,group_id=1023,default_permissions,allow_other 0 0
答案 2 :(得分:0)
看一下答案here。它也使用规范路径,但以稍微不同的方式可能对你有用
答案 3 :(得分:0)
在较新的Android版本中,SD存储可从多个路径获得,例如:
/storage/emulated/0
/storage/emulated/legacy (root account most of the time)
/sdcard
/data/media
如果你检查这些路径所在的设备,有些设备在不同的设备上(因为保险丝'虚拟文件系统),因此他们的规范路径不会导致相同的文件路径,即使它们实际上是相同的文件。
此外,似乎在Marshmallow上,事情变得更糟,甚至/ sys(完整的重定向/链接)下的文件路径都没有正确报告,并且getCanonicalPath()返回原始路径而不是实际的规范路径。
虽然给定文件路径上的ls -l(或readlink)将显示实际的规范路径,但API不再起作用。不幸的是,运行readlink或ls -l非常慢(当shell已经运行时平均为130ms),与已经很慢但速度更快的getCanonicalPath()相比,这很可惜。
结论,getCanonicalPath已被破坏并且一直被破坏。