10

在我的应用程序中,用户选择文件。在内部,我存储有关文件的信息,我根据文件路径对其进行键控。下次使用该文件时,我会处理存储的信息。麻烦的是我实例化我的文件:

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

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

问题是当其他应用程序使用 Intent 中的文件路径启动我的应用程序时,它们发送的路径往往看起来像:“/mnt/sdcard/test.txt”。

有没有一种聪明的策略来消除这两条路径的歧义?可能我应该以不同的方式实例化我的文件?

编辑:

问题是,这两个文件的两个规范路径不相等。对于以下内容,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();
4

4 回答 4

5

首先,获取外部路径的唯一正确方法是在 Android中使用getExternalStorageDirectoryand other 。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默认为路径。因此,不同的设备可能包含不同的外部存储路径。

正如外部存储技术信息所说,您可以通过设置 init.rc 文件来自定义设备的存储。例如在默认的金鱼之一中:

export EXTERNAL_STORAGE /mnt/sdcard
mkdir /mnt/sdcard 0000 system system
symlink /mnt/sdcard /sdcard

如果你使用getExternalStorageDirectory你会得到/mnt/sdcard, but/sdcard是一个指向那个目录的符号链接。

因此,在您的情况下, init.rc 可能包含:

export EMULATED_STORAGE_TARGET /storage/emulated
symlink /storage/emulated/0 /mnt/sdcard

所以它们不是模棱两可的,它们实际上是相同的。

我认为getCanonicalPath()可能适用于您的绝大多数用例。

规范路径名是绝对且唯一的。规范形式的精确定义取决于系统。如果需要,此方法首先将此路径名转换为绝对形式,就像调用 getAbsolutePath() 方法一样,然后以系统相关的方式将其映射到其唯一形式。这通常涉及删除冗余名称,例如“。” 和路径名中的“..”,解析符号链接(在 UNIX 平台上),并将驱动器号转换为标准大小写(在 Microsoft Windows 平台上)。

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

于 2013-04-09T05:35:34.000 回答
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
于 2013-04-15T10:32:27.627 回答
0

看看这里的答案。它还使用规范路径,但方式略有不同,可能对您有用

于 2013-04-14T04:12:42.383 回答
0

在较新的 Android 版本上,SD 存储可通过多种途径获得,例如:

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

如果您检查这些路径位于哪个设备上,有些设备位于不同的设备上(因为 fuse 'virtual' 文件系统),因此即使它们实际上是相同的文件,获取它们的规范路径也不会导致相同的文件路径。

此外,在 Marshmallow 上,情况似乎变得更糟,甚至 /sys 下的文件路径(充满重定向/链接)都没有正确报告,并且 getCanonicalPath() 返回原始路径而不是实际的规范路径。

虽然给定文件路径上的 ls -l(或 readlink)将显示实际的规范路径,但 API 不再起作用。不幸的是,运行 readlink 或 ls -l 非常慢(shell 已经运行时平均为 130 毫秒),与已经很慢但快得多的 getCanonicalPath() 相比,这很遗憾。

结论,getCanonicalPath 坏了,而且一直坏。

于 2016-01-17T09:00:37.667 回答