2013-04-05 37 views
10

Trong ứng dụng của tôi, người dùng chọn tệp. Trong nội bộ, tôi lưu trữ thông tin về tệp mà tôi khóa dựa trên đường dẫn tệp. Tiếp theo thời gian tập tin được sử dụng, tôi làm công cụ với các thông tin được lưu trữ. Vấn đề là tôi nhanh chóng các file của tôi với:Android: định hướng đường dẫn tệp

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

Và sau đó, trên một thiết bị JB Đặc biệt, file1.getCanonicalPath() cho phép: "/storage/emulated/0/test.txt".

Sự cố là khi các ứng dụng khác khởi chạy ứng dụng của tôi bằng đường dẫn tệp trong Intent, đường dẫn họ gửi có xu hướng giống như: "/mnt/sdcard/test.txt".

Có chiến lược thông minh nào để phân biệt hai đường dẫn này không? Có lẽ tôi nên instantiating các tập tin của tôi một cách khác nhau?

Chỉnh sửa:

Vấn đề là hai đường dẫn cana cho hai tệp không bằng nhau. Cho dưới đây, 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

Cùng một tệp có 2 đường dẫn tuyệt đối khác nhau? Những đường dẫn này khác nhau và có thể tồn tại trên bộ nhớ mà không loại trừ lẫn nhau. Bạn có thể xây dựng trên kịch bản nhiều hơn một chút không? Làm thế nào là 'Intent' gửi cho bạn chuẩn bị và khi nào nó được gửi? –

+0

Tôi đã hy vọng rằng sẽ có một cách để cân bằng hai, bởi vì các đường dẫn khác nhau trỏ đến cùng một tệp. Ngoài ra, nếu tôi đánh giá Môi trường.getExternalStorageDirectory() là "/ mnt/sdcard /" thay vì là "/ storage/emulated/0", nó sẽ làm giảm sự xuất hiện của vấn đề này (nhưng tôi không biết làm thế nào để làm điều này) – ab11

Trả lời

5

Thứ nhất, đúng cách duy nhất để có con đường bên ngoài được sử dụng getExternalStorageDirectorygetExternalStorageXXX khác trong Android.

Android đầu tiên sẽ cố gắng giải quyết biến hai hệ thống:

String rawExternalStorage = System.getenv(ENV_EXTERNAL_STORAGE); 
String rawEmulatedStorageTarget = System.getenv(ENV_EMULATED_STORAGE_TARGET); 

trong khi ENV_EXTERNAL_STORAGE = "EXTERNAL_STORAGE"ENV_EMULATED_STORAGE_TARGET = "EMULATED_STORAGE_TARGET". Nếu biến số EMULATED_STORAGE_TARGET được thiết lập, có nghĩa là thiết bị có bộ nhớ giả lập thì đường dẫn lưu trữ sẽ là EMULATED_STORAGE_TARGET. (Sau Android 4.2, nó hỗ trợ bộ nhớ ngoài nhiều người dùng, sau đó sẽ có một/0 hoặc 0 sau đường dẫn) nó chưa được đặt và EXTERNAL_STORAGE được đặt, đường dẫn sẽ là EXTERNAL_STORAGE. Nếu cả hai không được đặt, đường dẫn sẽ là /storage/sdcard0 theo mặc định. Vì vậy, các thiết bị khác nhau có thể chứa các đường dẫn khác nhau cho bộ nhớ ngoài.

External Storage Technical Information cho biết, bạn có thể tùy chỉnh bộ nhớ của thiết bị bằng cách thiết lập tệp init.rc. Ví dụ trong cá vàng một mặc định:

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

Nếu bạn sử dụng getExternalStorageDirectory bạn sẽ nhận được /mnt/sdcard, nhưng /sdcard là một liên kết tượng trưng đến thư mục đó.

Vì vậy, trong trường hợp của bạn, init.rc có thể chứa:

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

Vì vậy, họ không phải là mơ hồ, họ có thực sự như vậy.

Tôi nghĩ rằng getCanonicalPath() có thể hoạt động cho đại đa số các trường hợp sử dụng của bạn.

Tên đường dẫn chuẩn là tuyệt đối và duy nhất. Định nghĩa chính xác về dạng thức kinh điển phụ thuộc vào hệ thống. Phương pháp này đầu tiên chuyển đổi tên đường dẫn này thành biểu mẫu tuyệt đối nếu cần, như thể bằng cách gọi phương thức getAbsolutePath() và sau đó ánh xạ nó tới biểu mẫu duy nhất của nó theo cách phụ thuộc vào hệ thống. Điều này thường bao gồm việc xóa các tên dự phòng như "." và ".."Từ tên đường dẫn, giải quyết liên kết tượng trưng (trên nền tảng UNIX), và chuyển đổi ký tự ổ đĩa để một trường hợp tiêu chuẩn (trên nền tảng Microsoft Windows).

Mỗi tên đường dẫn mà biểu thị một tập tin hiện có hoặc thư mục có một hình thức độc đáo kinh điển Mỗi tên đường dẫn có nghĩa là một tập tin không tồn tại hoặc một thư mục cũng có một dạng kinh điển duy nhất. Tương tự, hình thức kinh điển của tên đường dẫn của tệp tin hoặc thư mục hiện có có thể khác với dạng hợp quy của cùng một tên sau khi tệp hoặc thư mục bị xóa.

+3

rắc rối là, cho hai đường dẫn "/mnt/sdcard/test.txt" và "/storage/emulated/0/test.txt": Tệp tệp1 = Tệp mới (path1), Tệp tệp 2 = Tệp mới (đường dẫn2). file1.getCanonicalPath(). bằng (file2.getCanoniclaPath()) đánh giá sai, mặc dù chúng trỏ đến cùng một tệp. – ab11

+0

Bạn có chắc là họ tham chiếu đến cùng một tệp không? Việc sửa đổi một tệp sẽ ảnh hưởng đến một tệp khác? Tôi đã thử nghiệm trên thiết bị của tôi, nó hoạt động. @ ab11 – StarPinkER

+0

FWIW, phiên bản Android mới hơn mà bạn sử dụng, điều tồi tệ nhất nó nhận được và điều đó không chỉ hợp lệ trên kho lưu trữ, mà còn có các đường dẫn khác như/sys! Trên Marshmallow, getCanonicalPath trả về đường dẫn ban đầu hầu hết thời gian, mặc dù ls -l hiển thị đường dẫn là một liên kết. Chỉ có giải pháp tôi tìm thấy là sử dụng một trình bao để chạy lệnh ls -l hoặc readlink. – 3c71

0

Hãy xem câu trả lời here. Nó cũng sử dụng đường dẫn kinh điển, nhưng theo một cách hơi khác có thể phù hợp với bạn

+0

Nếu thư mục chưa được tạo, thì getCanonicalPath() có lẽ sẽ trả lại cùng một đường dẫn như đã được truyền cho hàm tạo tệp, cũng như OP đang báo cáo cho các tệp. Tôi nghĩ rằng chìa khóa có lẽ là để gọi phương thức sau khi tập tin được tạo ra. – Carl

2

Có lẽ không có giải pháp dễ dàng cho việc này. Vấn đề là dường như có hai điểm gắn kết khác nhau trong hệ thống tệp thực sự trỏ đến cùng một vị trí. File.getCanonicalPath() chỉ có thể giải quyết các liên kết tượng trưng nhưng không chỉ các điểm gắn kết khác nhau.

Ví dụ trên Nexus của tôi 4 mã này:

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()); 

in

file 1: /storage/emulated/0/Android 
file 2: /storage/emulated/legacy/Android 

tôi đã sử dụng mã này để exec "gắn kết" và in kết quả:

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(); 

Các sản lượng có liên quan là:

/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

Vâng, đó là vấn đề. Bộ nhớ đa người dùng của Android 4.2 thực sự, thực sự phiền hà –

+0

Sử dụng bộ giả lập AOSP bộ gen, chỉ có một điểm gắn kết,/mnt/shell/emulated, nhưng getCanonicalPath vẫn không hoạt động. – Harvey

+0

@Harvey, tôi không thể xác nhận. Genymotion Nexus 4 API 19 hiển thị hai điểm gắn kết,/lưu trữ/mô phỏng/0 và/lưu trữ/mô phỏng/di sản. – devconsole

0

Trong các phiên bản Android mới hơn, lưu trữ SD có sẵn từ nhiều đường dẫn, ví dụ:

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

Nếu bạn kiểm tra trên thiết bị nào những con đường đang nằm, một số là trên các thiết bị khác nhau (vì cầu chì 'ảo' hệ thống tập tin), do đó nhận được đường dẫn kinh điển của họ không dẫn đến cùng một đường dẫn tệp, mặc dù chúng thực sự là cùng một tệp. Hơn nữa, có vẻ như trên Marshmallow, mọi thứ trở nên tồi tệ hơn và thậm chí đường dẫn tệp dưới/sys (đầy đủ các chuyển hướng/liên kết) không được báo cáo đúng và getCanonicalPath() trả về đường dẫn ban đầu thay vì đường dẫn kinh điển thực tế.

Trong khi ls -l (hoặc readlink) trên đường dẫn tệp đã cho sẽ hiển thị đường dẫn kinh điển thực tế, API không hoạt động nữa. Thật không may chạy một readlink hoặc ls -l là rất chậm (trung bình 130ms khi shell đã chạy), so với một getCanonicalPath đã chậm, nhưng nhanh hơn nhiều(), nó là một điều đáng tiếc.

Kết luận, getCanonicalPath bị hỏng và luôn bị hỏng.

Các vấn đề liên quan