1. Thay đổi
- Quyền truy cập vào bộ nhớ ngoài của ứng dụng: ứng dụng được cấp quyền truy cập vào bộ nhớ ngoài theo phạm vi của ứng dụng, phạm vi này gọi là scoped storage (Khác với trước đây là ứng dụng khá là thoải mái trong việc truy cập vào những file lưu ở bộ nhớ ngoài )
– Các file dành riêng cho ứng dụng thì truy cập thông quagetExternalFilesDir()
– Các file dạng Photo, Video hoặc Audio thì nên sử dụng media store.
2. Hành động
2.1 Tạm thời
Nếu không muốn áp dụng những thay đổi này thì trong file
AndroidManifest.xml
thêm thuộc tính cho<application>
android:requestLegacyExternalStorage=”true”
Tuy nhiên cách này chỉ là tạm thời áp dụng với các ứng dụng target Android 10 hoặc thấp hơn. Nếu target từ Android 11 và cao hơn thì thuộc tính này không có tác dụng. Tức là bạn thực sự cần chuẩn bị để migrate những phần data bị ảnh hưởng bởi thay đổi này.
2.2 Lâu dài
2.2.1 External storage
- Sử dụng
Context.getExternalFilesDir()
,getExternalCacheDir()
để thay thế choEnvironment.getExternalStorageDirectory()
- Ví dụ về đường dẫn 1 file ảnh được lưu ở bộ nhớ ngoài:
–Android 10 và cao hơn:
/storage/emulated/0/Android/data/package_app/files/Pictures/IMG_20201118_113434.jpg
–Android thấp hơn 10:
storage/emulated/0/Pictures/IMG_20201118_113434.jpg
2.2.1 Media Store
Thực chất là 1 cơ sở dữ liệu dạng SQL, do vậy có thể dễ dàng thực hiện một số thao tác insert, update, delete hoặc query.
Ví dụ 1 ảnh được lưu trong Media Store tại đường dẫn và uri tương ứng:
path = storage/emulated/0/Pictures/IMG_20201118_113434.jpg
uri = content://media/external/images/media/47
- Lưu ý:
– Không thể sử dụng các thao tác vào ra file thông thường như FileInputStream()
, FileOutputStream()
để thao tác trên nhưng file này. Nếu tiếp tục sẽ sinh ra ngoại lệ FileNotFoundException (open failed: EACCES permission denied)
– Hãy sử dụng ContentResolver
để thao tác trên những file này. Tham khảo openInputStream
, openOutputStream
Sử dụng với Glide
Với những ảnh lưu trong Media Store,Glide.load(path)
sẽ không hoạt động, thay vào đó hãy sử dụngGlide.load(uri)
.Một số đoạn code thông dụng
– Lấy danh sách Image
1 |
|
– Kiểm tra 1 file đã tồn tại trong Media Store
1 | fun exist(Context context, String fileName): Boolean { |
– Get uri của 1 file
1 | fun getUriByFileName(Context context, String fileName): Uri { |
- Xem thêm tại đây