2013-04-05 91 views
10

在我的應用程序中,用戶選擇文件。在內部,我存儲關於文件的信息,這是我基於文件路徑的關鍵。下一次使用該文件時,我會使用存儲的信息進行操作。麻煩的是我實例化我的文件有:Android:明確文件路徑

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

然後,特定JB設備上,file1.getCanonicalPath()給出: 「/storage/emulated/0/test.txt」。

麻煩的是,當其他應用程序啓動我的應用程序文件路徑在意圖,他們發送的路徑往往是這樣的:「/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

相同的文件有2個不同的絕對路徑?這些路徑是不同的,可以存儲在存儲中而不會互相排斥。你能詳細說明一下情況嗎? 「意圖」如何發送給您,並準備何時發送? – 2013-04-08 17:04:56

+0

我希望有一種方法可以將兩者等同起來,因爲不同的路徑指向同一個文件。或者,如果我評估環境。getExternalStorageDirectory()作爲「/ mnt/sdcard /」而不是作爲「/ storage/emulated/0」,它會減少這個問題的發生(但我不知道該怎麼辦) – ab11 2013-04-08 17:14:32

回答

5

首先,要獲得外部路徑的唯一正確途徑是使用getExternalStorageDirectory和Android的其他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平臺)。

是表示現有的文件或目錄具有獨特的 規範形式每個路徑表示不存在的文件或目錄的每個路徑名也具有唯一的規範形式。在文件或目錄爲 之後,不存在的文件或目錄的路徑名的規範形式可以不同於 相同路徑名的規範形式類似地,現有的 文件或目錄的路徑名的規範形式可能不同於規範形式刪除文件或目錄後的路徑名相同的 。

+3

麻煩的是,給出了兩條路徑「/mnt/sdcard/test.txt」和「/storage/emulated/0/test.txt」:文件file1 =新文件(path1),文件文件2 =新文件(path2)。 file1.getCanonicalPath()。equals(file2.getCanoniclaPath())的計算結果爲false,儘管它們指向同一個文件。 – ab11 2013-04-09 12:54:17

+0

你確定他們引用了同一個文件嗎?修改一個文件會影響另一個文件?我在我的設備上測試過,它可以工作。 @ ab11 – StarPinkER 2013-04-14 03:12:47

+0

FWIW,您最近使用的Android版本,最糟糕的是它不僅在存儲上有效,而且還在/ sys文件夾等其他路徑中有效!在Marshmallow中,getCanonicalPath大部分時間返回原始路徑,即使ls -l顯示路徑爲鏈接。我找到的唯一解決方案是使用shell來運行ls -l或readlink命令。 – 3c71 2016-01-17 08:54:13

0

看看答案here。它也使用規範路徑,但方式稍有不同,可能適用於您

+0

如果該文件夾尚未創建,則getCanonicalPath()可能會返回與傳遞給File構造函數相同的路徑,就像OP報告文件一樣。我認爲關鍵可能是在創建文件後調用方法。 – Carl 2013-04-15 08:16:01

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 

我用這個碼給exec 「裝載」 和打印輸出:

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 
+1

是的,這是問題所在。 Android 4.2的多用戶存儲真的很麻煩 – 2013-07-12 11:35:43

+0

使用genymotion AOSP模擬器,只有一個掛載點/ mnt/shell/emulated,但getCanonicalPath仍然不起作用。 – Harvey 2015-02-20 19:31:47

+0

@哈維,我無法證實。 Genymotion Nexus 4 API 19顯示兩個安裝點,/ storage/emulated/0和/ storage/emulated/legacy。 – devconsole 2015-02-22 20:06:50

0

在較新的Android版本,SD存儲是可從許多路徑,例如:

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

如果選中使用的裝置的那些路徑的位置,有些是在不同的設備(因爲熔絲的「虛擬」文件系統),因此得到他們的規範路徑不會導致相同的文件路徑,即使它們實際上是相同的文件。

此外,它似乎在棉花糖,情況變得更糟,甚至/ sys(充滿重定向/鏈接)文件路徑不正確報告和getCanonicalPath()返回原始路徑,而不是實際的規範路徑。

雖然給定文件路徑上的ls -l(或readlink)將顯示實際的規範路徑,但該API不再有效。不幸的是,運行readlink或者ls -l的速度非常慢(當shell已經運行時平均運行時間爲130ms),相比之下,getCanonicalPath()已經很慢了,但是速度更快,這很遺憾。

結論,getCanonicalPath被打破並且一直被打破。