Android:消除文件path

在我的应用程序中,用户select文件。 在内部,我存储有关基于文件path的文件的信息。 下一次使用该文件时,我会使用存储的信息进行操作。 麻烦的是我实例化我的文件:

File file1 = new File(Environment.getExternalStorageDirectory() + "/test.txt");

然后,在一个特定的JB设备上,file1.getCanonicalPath()给出:“/storage/emulated/0/test.txt”。

麻烦的是,当其他应用程序在Intent中启动我的应用程序时,它们发送的path通常如下所示:“/mnt/sdcard/test.txt”。

有没有明智的策略来消除这两条路? 可能我应该实例化我的文件不同?

编辑:

麻烦的是,这两个文件的两个canaonicalpath是不相等的。 对于以下, 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(); 

Solutions Collecting From Web of "Android:消除文件path"

首先,获取外部path的唯一正确方法是使用Android中的getExternalStorageDirectory和其他getExternalStorageXXX

Android将首先尝试parsing两个系统variables:

 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_TARGETvariables,则表示设备已经模拟存储,则存储path为EMULATED_STORAGE_TARGET (在Android 4.2之后,它支持多用户外部存储,那么在path之后会有一个/ 0或0)。它没有设置EXTERNAL_STORAGE设置,path将EXTERNAL_STORAGE 。 如果两者都没有设置,默认path是/storage/sdcard0 。 所以不同的设备可能包含不同的外部存储path。

正如外部存储技术信息所说,您可以通过设置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()可能适用于绝大多数的用例。

规范的path名既是绝对的也是唯一的。 规范forms的确切定义是依赖于系统的。 如果需要,此方法首先将此path名转换为绝对forms,就像调用getAbsolutePath()方法一样,然后以依赖于系统的方式将其映射到其唯一forms。 这通常涉及删除多余的名称,如“。” 和“..”,parsing符号链接(在UNIX平台上),并将驱动器号转换为标准大小写(在Microsoft Windows平台上)。

表示现有文件或目录的每个path名都具有唯一的规范forms。 表示不存在的文件或目录的每个path名也具有唯一的规范forms。 不存在的文件或目录的path名的规范forms可能与创build文件或目录后相同path名的规范forms不同。 类似地,在删除文件或目录后,现有文件或目录的path名的规范forms可能与相同path名的规范forms不同。

这可能不是一个简单的解决scheme。 问题是显然在文件系统中有两个不同的挂载点实际上指向相同的位置。 File.getCanonicalPath()只能parsing符号链接,不能parsing不同的挂载点。

例如在我的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 

我用这个代码来执行“挂载”并打印输出:

 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 

看看这里的答案。 它也使用规范的path,但以稍微不同的方式,可能会为你工作

在较新的Android版本中,SD存储可以从许多path获得,例如:

 /storage/emulated/0 /storage/emulated/legacy (root account most of the time) /sdcard /data/media 

如果你检查这些path位于哪个设备上,有些设备位于不同的设备上(因为fuse是“虚拟”的文件系统),因此,即使它们实际上是相同的文件,获得它们的规范path也不会导致相同的文件path。

此外,似乎在棉花糖,情况变得更糟,甚至/ sys(充满redirect/链接)文件path不正确报告和getCanonicalPath()返回原来的path,而不是实际的规范path。

虽然给定文件path上的ls -l(或readlink)将显示实际的规范path,但API不再适用。 不幸的是,运行一个readlink或者ls -l的速度非常慢(shell已经运行了130ms),相比之下getCanonicalPath()已经很慢了,但是速度更快了,真遗憾。

结论,getCanonicalPath被打破,一直被打破。