Tóm lược
Bạn có thể cấp quyền truy cập đọc / ghi cho thẻ SD bên ngoài ở các cấp api khác nhau ( API23 + tại thời điểm chạy ).
Kể từ KitKat, các quyền là không cần thiết nếu bạn sử dụng các thư mục dành riêng cho ứng dụng, nếu không thì bắt buộc .
Cách phổ biến:
Các lịch sử nói rằng không có cách phổ biến để viết vào thẻ SD bên ngoài nhưng vẫn tiếp tục ...
Thực tế này được phân tích bằng những ví dụ về cấu hình bộ nhớ ngoài cho thiết bị .
Cách dựa trên API:
Trước KitKat, hãy thử sử dụng Doomsknight phương pháp 1, phương pháp 2 nếu không.
Yêu cầu quyền trong tệp kê khai (Api <23) và tại thời điểm chạy (Api> = 23).
Cách đề xuất:
ContextCompat.getExternalFilesDirs giải quyết lỗi truy cập khi bạn không cần chia sẻ tệp.
Cách chia sẻ an toàn là sử dụng nhà cung cấp nội dung hoặc Khung truy cập lưu trữ mới .
Cách nhận biết quyền riêng tư:
Kể từ Android Q Beta 4 , theo mặc định, các ứng dụng nhắm mục tiêu đến Android 9 (API cấp 28) trở xuống không có thay đổi.
Các ứng dụng nhắm mục tiêu Android Q theo mặc định (hoặc chọn tham gia) được cung cấp chế độ xem đã lọc vào bộ nhớ ngoài.
1. Câu trả lời ban đầu.
Cách phổ biến để ghi vào thẻ SD bên ngoài trên Android
Không có cách phổ biến nào để ghi vào thẻ SD bên ngoài trên Android do những thay đổi liên tục :
Pre-KitKat: nền tảng Android chính thức chưa hỗ trợ thẻ SD trừ một số ngoại lệ.
KitKat: API được giới thiệu cho phép ứng dụng truy cập tệp trong thư mục dành riêng cho ứng dụng trên thẻ SD.
Lollipop: đã thêm các API để cho phép các ứng dụng yêu cầu quyền truy cập vào các thư mục do các nhà cung cấp khác sở hữu.
Nougat: cung cấp một API đơn giản hóa để truy cập các thư mục lưu trữ bên ngoài phổ biến.
... Thay đổi quyền riêng tư của Android Q: Bộ nhớ trong phạm vi ứng dụng và phạm vi phương tiện
Cách tốt hơn để cấp quyền truy cập đọc / ghi cho thẻ SD bên ngoài ở các cấp API khác nhau là gì
Dựa trên câu trả lời của Doomsknight và của tôi , và các bài đăng trên blog của Dave Smith và Mark Murphy: 1 , 2 , 3 :
2. Cập nhật câu trả lời.
Cập nhật 1 . Tôi đã thử Phương pháp 1 từ câu trả lời của Doomknight nhưng không có kết quả:
Như bạn có thể thấy, tôi đang kiểm tra các quyền trong thời gian chạy trước khi cố gắng ghi trên SD ...
Tôi sẽ sử dụng các thư mục dành riêng cho ứng dụng để tránh vấn đề về câu hỏi đã cập nhật của bạn và ContextCompat.getExternalFilesDirs()
sử dụng tài liệu getExternalFilesDir làm tài liệu tham khảo.
Cải thiện phương pháp heuristics để xác định những gì đại diện cho phương tiện di động dựa trên các cấp api khác nhau nhưandroid.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT
... Nhưng tôi gặp lỗi truy cập, đã thử trên hai thiết bị khác nhau: HTC10 và Shield K1.
Hãy nhớ rằng Android 6.0 hỗ trợ thiết bị lưu trữ di động và các ứng dụng của bên thứ ba phải thông qua Khung truy cập bộ nhớ . Các thiết bị HTC10 và Shield K1 của bạn có thể là API 23.
Nhật ký của bạn hiển thị quyền bị từ chối truy cập ngoại lệ/mnt/media_rw
, chẳng hạn như bản sửa lỗi này cho API 19+:
<permission name="android.permission.WRITE_EXTERNAL_STORAGE" >
<group gid="sdcard_r" />
<group gid="sdcard_rw" />
<group gid="media_rw" />
</permission>
Tôi chưa bao giờ thử nó vì vậy tôi không thể chia sẻ mã nhưng tôi sẽ tránh for
cố gắng viết trên tất cả các thư mục trả về và tìm kiếm thư mục lưu trữ có sẵn tốt nhất để ghi vào dựa trên dung lượng còn lại .
Có lẽ sự thay thế của Gizm0 cho getStorageDirectories()
phương pháp của bạn đó là một điểm khởi đầu tốt.
ContextCompat.getExternalFilesDirs
giải quyết vấn đề nếu bạn không cần quyền truy cập vào các thư mục khác .
3. Android 1.0 .. Pre-KitKat.
Trước KitKat, hãy thử sử dụng Doomsknight method 1 hoặc đọc phản hồi này của Gnathonic.
public static HashSet<String> getExternalMounts() {
final HashSet<String> out = new HashSet<String>();
String reg = "(?i).*vold.*(vfat|ntfs|exfat|fat32|ext3|ext4).*rw.*";
String s = "";
try {
final Process process = new ProcessBuilder().command("mount")
.redirectErrorStream(true).start();
process.waitFor();
final InputStream is = process.getInputStream();
final byte[] buffer = new byte[1024];
while (is.read(buffer) != -1) {
s = s + new String(buffer);
}
is.close();
} catch (final Exception e) {
e.printStackTrace();
}
final String[] lines = s.split("\n");
for (String line : lines) {
if (!line.toLowerCase(Locale.US).contains("asec")) {
if (line.matches(reg)) {
String[] parts = line.split(" ");
for (String part : parts) {
if (part.startsWith("/"))
if (!part.toLowerCase(Locale.US).contains("vold"))
out.add(part);
}
}
}
}
return out;
}
Thêm mã tiếp theo vào của bạn AndroidManifest.xml
và đọc Nhận quyền truy cập vào bộ nhớ ngoài
Quyền truy cập vào bộ nhớ ngoài được bảo vệ bởi các quyền khác nhau của Android.
Bắt đầu từ Android 1.0, quyền ghi được bảo vệ với WRITE_EXTERNAL_STORAGE
quyền .
Bắt đầu từ Android 4.1, quyền truy cập đọc được bảo vệ với READ_EXTERNAL_STORAGE
quyền.
Để ... ghi tệp trên bộ nhớ ngoài, ứng dụng của bạn phải có ... quyền hệ thống:
<manifest ...>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
</manifest>
Nếu bạn cần cả hai ..., bạn chỉ cần yêu cầu sự WRITE_EXTERNAL_STORAGE
cho phép.
Đọc lời giải thích Mark Murphy của và đề nghị Dianne Hackborn và Dave Smith bài viết
- Cho đến Android 4.4, không có hỗ trợ chính thức cho phương tiện di động trong Android, Bắt đầu từ KitKat, khái niệm bộ nhớ ngoài "chính" và "phụ" xuất hiện trong API FMW.
- Các ứng dụng trước đây chỉ dựa vào lập chỉ mục MediaStore, vận chuyển cùng với phần cứng hoặc kiểm tra các điểm gắn kết và áp dụng một số phương pháp phỏng đoán để xác định những gì đại diện cho phương tiện di động.
Bỏ qua ghi chú tiếp theo do lỗi, nhưng hãy thử sử dụng ContextCompat.getExternalFilesDirs()
:
- Kể từ Android 4.2, Google đã yêu cầu các nhà sản xuất thiết bị khóa phương tiện di động để bảo mật (hỗ trợ nhiều người dùng) và các thử nghiệm mới đã được thêm vào 4.4.
- Vì KitKat
getExternalFilesDirs()
và các phương thức khác đã được thêm vào để trả về một đường dẫn có thể sử dụng trên tất cả các ổ lưu trữ có sẵn (Mục đầu tiên được trả về là ổ chính).
- Bảng dưới đây cho biết nhà phát triển có thể cố gắng làm gì và KitKat sẽ phản hồi như thế nào:
Lưu ý: Bắt đầu với Android 4.4, các quyền này không bắt buộc nếu bạn chỉ đọc hoặc ghi các tệp riêng tư cho ứng dụng của mình. Để biết thêm thông tin ..., hãy xem lưu các tệp ở chế độ riêng tư của ứng dụng .
<manifest ...>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
android:maxSdkVersion="18" />
</manifest>
Cũng đọc lời giải thích của Paolo Rovelli và thử sử dụng giải pháp của Jeff Sharkey kể từ KitKat:
Trong KitKat hiện có một API công khai để tương tác với các thiết bị lưu trữ dùng chung thứ cấp này.
Phương thức Context.getExternalFilesDirs()
và
mới Context.getExternalCacheDirs()
có thể trả về nhiều đường dẫn, bao gồm cả thiết bị chính và thiết bị phụ.
Sau đó, bạn có thể lặp lại chúng và kiểm tra Environment.getStorageState()
và
File.getFreeSpace()
xác định nơi tốt nhất để lưu trữ tệp của mình.
Các phương pháp này cũng có sẵn ContextCompat
trong thư viện support-v4.
Bắt đầu từ Android 4.4, chủ sở hữu, nhóm và chế độ của tệp trên thiết bị lưu trữ bên ngoài hiện được tổng hợp dựa trên cấu trúc thư mục. Điều này cho phép các ứng dụng quản lý các thư mục dành riêng cho gói của chúng trên bộ nhớ ngoài mà không yêu cầu chúng có WRITE_EXTERNAL_STORAGE
quyền rộng rãi
. Ví dụ: ứng dụng có tên gói com.example.foo
hiện có thể tự do truy cập
Android/data/com.example.foo/
trên các thiết bị lưu trữ bên ngoài mà không cần quyền. Các quyền tổng hợp này được thực hiện bằng cách gói các thiết bị lưu trữ thô trong một daemon FUSE.
Với KitKat, cơ hội của bạn cho một "giải pháp hoàn chỉnh" mà không cần root là khá nhiều:
Dự án Android chắc chắn đã hoàn thành ở đây. Không ứng dụng nào có quyền truy cập đầy đủ vào thẻ SD bên ngoài:
- trình quản lý tệp: bạn không thể sử dụng chúng để quản lý thẻ SD bên ngoài của mình. Trong hầu hết các lĩnh vực, họ chỉ có thể đọc chứ không thể viết.
- ứng dụng phương tiện: bạn không thể gắn thẻ lại / sắp xếp lại bộ sưu tập phương tiện của mình nữa, vì những ứng dụng đó không thể ghi vào đó.
- ứng dụng văn phòng: khá giống nhau
Nơi duy nhất các ứng dụng của bên thứ 3 được phép ghi trên thẻ bên ngoài của bạn là "thư mục riêng của chúng" (tức là
/sdcard/Android/data/<package_name_of_the_app>
).
Các cách duy nhất để thực sự khắc phục yêu cầu nhà sản xuất (một số trong số họ đã sửa nó, ví dụ như Huawei với bản cập nhật Kitkat của họ cho P6) - hoặc root ... (Giải thích của Izzy tiếp tục ở đây)
5. Android 5.0 đã giới thiệu các thay đổi và lớp trợ giúp DocumentFile .
getStorageState
Đã thêm vào API 19, không dùng nữa trong API 21,
sử dụnggetExternalStorageState(File)
Đây là hướng dẫn tuyệt vời để tương tác với Khung truy cập lưu trữ trong KitKat.
Tương tác với các API mới trong Lollipop rất giống nhau (theo giải thích của Jeff Sharkey) .
6. Android 6.0 Marshmallow giới thiệu một mô hình quyền thời gian chạy mới .
Quyền yêu cầu trong thời gian chạy nếu mức API 23+ và đọc Yêu cầu Quyền tại Run Time
Bắt đầu từ Android 6.0 (API cấp 23), người dùng cấp quyền cho ứng dụng trong khi ứng dụng đang chạy, không phải khi họ cài đặt ứng dụng ... hoặc cập nhật ứng dụng ... người dùng có thể thu hồi quyền.
int permissionCheck = ContextCompat.checkSelfPermission(thisActivity,
Manifest.permission.WRITE_EXTERNAL_STORAGE);
Android 6.0 giới thiệu mô hình quyền thời gian chạy mới trong đó các ứng dụng yêu cầu các khả năng khi cần thiết trong thời gian chạy. Vì mô hình mới bao gồm các READ/WRITE_EXTERNAL_STORAGE
quyền nên nền tảng cần tự động cấp quyền truy cập vào bộ nhớ mà không giết hoặc khởi động lại các ứng dụng đã chạy. Nó thực hiện điều này bằng cách duy trì ba chế độ xem riêng biệt của tất cả các thiết bị lưu trữ được gắn kết:
- / mnt / runtime / default được hiển thị cho các ứng dụng không có quyền lưu trữ đặc biệt ...
- / mnt / runtime / read được hiển thị cho các ứng dụng có READ_EXTERNAL_STORAGE
- / mnt / runtime / write được hiển thị cho các ứng dụng có WRITE_EXTERNAL_STORAGE
7. Android 7.0 cung cấp một API đơn giản hóa để truy cập các bộ nhớ ngoài.
Quyền truy cập thư mục theo phạm vi
Trong Android 7.0, các ứng dụng có thể sử dụng các API mới để yêu cầu quyền truy cập vào các thư mục bộ nhớ ngoài cụ thể
, bao gồm các thư mục trên phương tiện di động như thẻ SD ...
Để biết thêm thông tin, hãy xem đào tạo về Quyền truy cập thư mục theo phạm vi .
Đọc các bài đăng của Mark Murphy: Hãy cẩn thận với quyền truy cập thư mục theo phạm vi . Nó không được dùng nữa trong Android Q :
Lưu ý rằng quyền truy cập thư mục theo phạm vi được thêm vào trong 7.0 không được dùng trong Android Q.
Cụ thể, createAccessIntent()
phương thức trên StorageVolume không được dùng nữa.
Họ đã thêm một createOpenDocumentTreeIntent()
có thể được sử dụng như một sự thay thế.
8. Android 8.0 Oreo .. Android Q Beta thay đổi.
Bắt đầu từ Android O , Khung truy cập bộ nhớ cho phép các nhà cung cấp tài liệu tùy chỉnh
tạo các bộ mô tả tệp có thể tìm kiếm cho các tệp nằm trong nguồn dữ liệu từ xa ...
Quyền ,
trước Android O , nếu một ứng dụng yêu cầu quyền trong thời gian chạy và quyền đã được cấp, hệ thống cũng cấp sai cho ứng dụng phần còn lại của các quyền thuộc cùng một nhóm quyền và đã được đăng ký trong tệp kê khai.
Đối với các ứng dụng nhắm mục tiêu Android O , hành vi này đã được sửa chữa. Ứng dụng chỉ được cấp các quyền mà ứng dụng đã yêu cầu rõ ràng. Tuy nhiên, khi người dùng cấp quyền cho ứng dụng, tất cả các yêu cầu cấp quyền tiếp theo trong nhóm quyền đó sẽ tự động được cấp.
Ví dụ, READ_EXTERNAL_STORAGE
và WRITE_EXTERNAL_STORAGE
...
Cập nhật: Bản phát hành Android Q beta trước đó tạm thời thay thế quyền READ_EXTERNAL_STORAGE
và WRITE_EXTERNAL_STORAGE
bằng các quyền chi tiết hơn, dành riêng cho phương tiện.
Lưu ý: Google đã giới thiệu các vai trò trên Beta 1 và xóa chúng khỏi tài liệu trước Beta 2 ...
Lưu ý: Các điều khoản cụ thể để các bộ sưu tập phương tiện truyền thông đã được giới thiệu trong releases- beta trước đó READ_MEDIA_IMAGES
, READ_MEDIA_AUDIO
và READ_MEDIA_VIDEO
- bây giờ là lỗi thời . Thêm thông tin:
Bài đánh giá Q Beta 4 (API cuối cùng) của Mark Murphy: Cái chết của bộ nhớ ngoài: Sự kết thúc của Saga (?)
"Cái chết phổ biến hơn sự sống. Mọi người đều chết, nhưng không phải ai cũng sống." - Andrew Sachs
9. Câu hỏi liên quan và câu trả lời được đề xuất.
Làm cách nào để nhận đường dẫn thẻ SD bên ngoài cho Android 4.0+?
mkdir () hoạt động khi bên trong bộ nhớ flash nội bộ, nhưng không hoạt động với thẻ SD?
Sự khác biệt giữa getExternalFilesDir và getExternalStorageDirectory ()
Tại sao getExternalFilesDirs () không hoạt động trên một số thiết bị?
Cách sử dụng API truy cập thẻ SD mới được trình bày cho Android 5.0 (Lollipop)
Ghi vào thẻ SD bên ngoài trong Android 5.0 trở lên
Quyền ghi trên thẻ SD của Android sử dụng SAF (Khung truy cập bộ nhớ)
SAFFAQ: Câu hỏi thường gặp về Khung truy cập lưu trữ
10. Các lỗi và vấn đề liên quan.
Lỗi: Trên Android 6, khi sử dụng getExternalFilesDirs, nó sẽ không cho phép bạn tạo tệp mới trong kết quả của nó
Ghi vào thư mục được trả về bởi getExternalCacheDir () trên Lollipop không thành công mà không có quyền ghi