Các phương pháp hay nhất và trường hợp sử dụng bộ nhớ Android

Để mang lại cho người dùng nhiều quyền kiểm soát hơn đối với tệp của họ và hạn chế tình trạng tệp lộn xộn, Android 10 đã cho ra mắt một mô hình bộ nhớ mới cho các ứng dụng có tên là bộ nhớ có giới hạn. Bộ nhớ có giới hạn thay đổi cách ứng dụng lưu trữ và truy cập vào các tệp trên bộ nhớ ngoài của thiết bị. Để giúp bạn di chuyển ứng dụng của mình nhằm hỗ trợ bộ nhớ có giới hạn, hãy làm theo các phương pháp hay nhất dành cho những trường hợp sử dụng bộ nhớ phổ biến được nêu trong hướng dẫn này. Các trường hợp sử dụng được sắp xếp theo hai danh mục: xử lý các tệp đa phương tiệnxử lý các tệp không phải đa phương tiện.

Trong nhiều trường hợp, ứng dụng của bạn sẽ tạo các tệp mà những ứng dụng khác không cần truy cập hoặc không được truy cập. Hệ thống cung cấp các vị trí lưu trữ dành riêng cho ứng dụng để quản lý các tệp như vậy.

Để tìm hiểu thêm về cách lưu trữ và truy cập vào các tệp trên Android, hãy xem hướng dẫn huấn luyện về bộ nhớ.

Xử lý các tệp đa phương tiện

Mục này mô tả một số trường hợp sử dụng phổ biến khi xử lý các tệp đa phương tiện (tệp video, hình ảnh và âm thanh) và giải thích tổng quát cách tiếp cận ứng dụng của bạn có thể sử dụng. Bảng sau đây tóm tắt từng trường hợp sử dụng trong số này và liên kết đến từng phần cung cấp thêm thông tin chi tiết.

Trường hợp sử dụng Tóm tắt
Hiển thị tất cả các tệp hình ảnh hoặc video Sử dụng cùng một phương pháp cho tất cả các phiên bản Android.
Hiển thị hình ảnh hoặc video từ một thư mục cụ thể Sử dụng cùng một phương pháp cho tất cả các phiên bản Android.
Truy cập thông tin vị trí từ ảnh Sử dụng một phương pháp nếu ứng dụng của bạn sử dụng bộ nhớ có giới hạn. Sử dụng một phương pháp khác nếu ứng dụng của bạn chọn không sử dụng bộ nhớ có giới hạn.
Xác định vị trí bộ nhớ cho các tệp mới tải xuống Sử dụng một phương pháp nếu ứng dụng của bạn sử dụng bộ nhớ có giới hạn. Sử dụng một phương pháp khác nếu ứng dụng của bạn chọn không sử dụng bộ nhớ có giới hạn.
Xuất các tệp đa phương tiện của người dùng sang một thiết bị Sử dụng cùng một phương pháp cho tất cả các phiên bản Android.
Sửa đổi hoặc xoá nhiều tệp đa phương tiện trong một thao tác Sử dụng một phương pháp dành cho Android 11. Đối với Android 10, hãy chọn không dùng bộ nhớ có giới hạn và dùng phương pháp này cho Android 9 trở xuống.
Nhập một hình ảnh đã tồn tại Sử dụng cùng một phương pháp cho tất cả các phiên bản Android.
Chụp một hình ảnh Sử dụng cùng một phương pháp cho tất cả các phiên bản Android.
Chia sẻ tệp đa phương tiện với các ứng dụng khác Sử dụng cùng một phương pháp cho tất cả các phiên bản Android.
Chia sẻ tệp đa phương tiện với một ứng dụng cụ thể Sử dụng cùng một phương pháp cho tất cả các phiên bản Android.
Truy cập tệp từ mã hoặc thư viện sử dụng đường dẫn tệp trực tiếp Sử dụng một phương pháp dành cho Android 11. Đối với Android 10, hãy chọn không dùng bộ nhớ có giới hạn và dùng phương pháp này cho Android 9 trở xuống.

Hiển thị các tệp hình ảnh hoặc video từ nhiều thư mục

Truy vấn một bộ sưu tập đa phương tiệnbằng API query(). Để lọc hoặc sắp xếp các tệp đa phương tiện, hãy điều chỉnh các tham số projection, selection, selectionArgssortOrder.

Hiển thị hình ảnh hoặc video từ một thư mục cụ thể

Sử dụng phương pháp sau:

  1. Làm theo các phương pháp hay nhất nêu trong phần Yêu cầu quyền cho ứng dụng, yêu cầu quyền READ_EXTERNAL_STORAGE.
  2. Truy xuất tệp đa phương tiện dựa trên giá trị của MediaColumns.DATA, chứa đường dẫn hệ thống tệp tuyệt đối đến mục phương tiện trên ổ đĩa.

Lưu ý: Khi truy cập vào một tệp đa phương tiện hiện có, bạn có thể sử dụng giá trị của cột DATA trong logic của mình. Đó là vì giá trị này có một đường dẫn tệp hợp lệ. Tuy nhiên, đừng cho rằng tệp đó luôn có sẵn. Hãy chuẩn bị để sẵn sàng xử lý mọi lỗi I/O dựa trên tệp có thể xảy ra.

Mặt khác, để tạo hoặc cập nhật một tệp đa phương tiện, đừng sử dụng cột DATA. Thay vào đó, hãy sử dụng cột DISPLAY_NAMERELATIVE_PATH.

Truy cập vào thông tin vị trí từ ảnh

Nếu ứng dụng của bạn sử dụng bộ nhớ có giới hạn, hãy làm theo các bước trong phần Thông tin vị trí trong ảnh chụp của hướng dẫn về bộ nhớ phương tiện.

Xác định vị trí bộ nhớ cho các tệp mới tải xuống

Nếu ứng dụng của bạn sử dụng bộ nhớ có giới hạn, hãy lưu ý vị trí bạn chọn lưu các tệp đa phương tiện mà bạn tải xuống.

Nếu các ứng dụng khác yêu cầu quyền truy cập vào tệp, hãy cân nhắc sử dụng bộ sưu tập đa phương tiện được xác định rõ cho tệp tải xuống hoặc bộ sưu tập tài liệu.

Trên Android 11 trở lên, các ứng dụng khác không thể truy cập vào các tệp bên trong thư mục dành riêng cho ứng dụng bên ngoài, ngay cả khi bạn sử dụng DownloadManager để tìm nạp các tệp này.

Xuất các tệp đa phương tiện của người dùng sang một thiết bị

Xác định vị trí mặc định thích hợp để lưu trữ tệp đa phương tiện của người dùng:

Sửa đổi hoặc xoá nhiều tệp đa phương tiện trong một thao tác

Kết hợp logic dựa trên các phiên bản Android mà ứng dụng của bạn chạy.

Chạy trên Android 11

Sử dụng phương pháp sau:

  1. Tạo một ý định đang chờ xử lý cho yêu cầu ghi hoặc xoá của ứng dụng bằng MediaStore.createWriteRequest() hoặc MediaStore.createTrashRequest() rồi nhắc người dùng cấp quyền chỉnh sửa tập hợp tệp bằng cách gọi ý định đó.
  2. Đánh giá phản hồi của người dùng:

    • Nếu quyền đã được cấp, hãy tiến hành sửa đổi hoặc xoá thao tác.
    • Nếu quyền chưa được cấp, hãy giải thích cho người dùng về lý do tính năng trong ứng dụng của bạn cần quyền đó.

Tìm hiểu thêm về cách quản lý các nhóm tệp đa phương tiện bằng các phương thức có trên Android 11 trở lên này.

Chạy trên Android 10

Nếu ứng dụng của bạn nhắm đến Android 10 (API cấp 29), hãy chọn không sử dụng bộ nhớ có giới hạn và tiếp tục sử dụng phương pháp cho Android 9 trở xuống để thực hiện thao tác này.

Chạy trên Android 9 trở xuống

Sử dụng phương pháp sau:

  1. Làm theo các phương pháp hay nhất nêu trong phần Yêu cầu quyền cho ứng dụng, yêu cầu quyền WRITE_EXTERNAL_STORAGE.
  2. Dùng API MediaStore để sửa đổi hoặc xoá các tệp đa phương tiện.

Nhập một hình ảnh đã tồn tại

Khi bạn muốn nhập một hình ảnh đã tồn tại (ví dụ: để sử dụng làm ảnh hồ sơ của người dùng), ứng dụng của bạn có thể sử dụng giao diện người dùng riêng cho thao tác hoặc có thể sử dụng bộ chọn hệ thống.

Trình bày giao diện người dùng của riêng bạn

Sử dụng phương pháp sau:

  1. Làm theo các phương pháp hay nhất nêu trong phần Yêu cầu quyền cho ứng dụng, yêu cầu quyền READ_EXTERNAL_STORAGE.
  2. Dùng API query() để truy vấn bộ sưu tập đa phương tiện.
  3. Hiển thị các kết quả trong giao diện người dùng tuỳ chỉnh của ứng dụng.

Sử dụng bộ chọn hệ thống

Sử dụng ý định ACTION_GET_CONTENT, yêu cầu người dùng chọn một hình ảnh cần nhập.

Nếu muốn lọc các loại hình ảnh mà bộ chọn hệ thống hiển thị để người dùng lựa chọn, bạn có thể sử dụng setType() hoặc EXTRA_MIME_TYPES.

Chụp một hình ảnh

Khi bạn muốn chụp một hình ảnh để dùng trong ứng dụng của mình (ví dụ: dùng làm ảnh hồ sơ của người dùng), hãy sử dụng ý định ACTION_IMAGE_CAPTURE để yêu cầu người dùng chụp ảnh bằng máy ảnh của thiết bị. Hệ thống lưu trữ ảnh đã chụp trong bảng MediaStore.Images.

Chia sẻ tệp đa phương tiện với các ứng dụng khác

Sử dụng phương thức insert() để thêm các bản ghi trực tiếp vào MediaStore. Để biết thêm thông tin, xem phần Thêm mục trong hướng dẫn về bộ nhớ phương tiện.

Chia sẻ tệp đa phương tiện với một ứng dụng cụ thể

Sử dụng thành phần FileProvider của Android, như mô tả trong hướng dẫn Thiết lập tính năng chia sẻ tệp.

Truy cập các tệp từ mã hoặc thư viện sử dụng đường dẫn tệp trực tiếp

Kết hợp logic dựa trên các phiên bản Android mà ứng dụng của bạn chạy.

Chạy trên Android 11

Sử dụng phương pháp sau:

  1. Làm theo các phương pháp hay nhất nêu trong phần Yêu cầu quyền cho ứng dụng, yêu cầu quyền READ_EXTERNAL_STORAGE.
  2. Truy cập các tệp bằng đường dẫn tệp trực tiếp.

Để biết thêm thông tin, hãy xem phần về cách mở tệp đa phương tiện bằng đường dẫn tệp trực tiếp.

Chạy trên Android 10

Nếu ứng dụng của bạn nhắm đến Android 10 (API cấp 29), hãy chọn không sử dụng bộ nhớ có giới hạn và tiếp tục sử dụng phương pháp cho Android 9 trở xuống để thực hiện thao tác này.

Chạy trên Android 9 trở xuống

Sử dụng phương pháp sau:

  1. Làm theo các phương pháp hay nhất nêu trong phần Yêu cầu quyền cho ứng dụng, yêu cầu quyền WRITE_EXTERNAL_STORAGE.
  2. Truy cập các tệp bằng đường dẫn tệp trực tiếp.

Xử lý các tệp không phải phương tiện

Mục này mô tả một số trường hợp sử dụng phổ biến khi xử lý các tệp không phải phương tiện và giải thích phương pháp cấp cao mà ứng dụng của bạn có thể sử dụng. Bảng sau đây tóm tắt từng trường hợp sử dụng trong số này và liên kết đến từng phần cung cấp thêm thông tin chi tiết.

Trường hợp sử dụng Tóm tắt
Mở một tệp tài liệu Sử dụng cùng một phương pháp cho tất cả các phiên bản Android.
Ghi vào tệp trên phương tiện bộ nhớ phụ Sử dụng một phương pháp dành cho Android 11. Sử dụng một phương pháp khác cho các phiên bản Android cũ hơn.
Di chuyển các tệp hiện có từ một vị trí lưu trữ cũ Di chuyển tệp sang bộ nhớ có giới hạn khi có thể. Chọn không sử dụng bộ nhớ có giới hạn cho Android 10 khi cần thiết.
Chia sẻ nội dung với các ứng dụng khác Sử dụng cùng một phương pháp cho tất cả các phiên bản Android.
Lưu các tệp không phải phương tiện vào bộ nhớ đệm Sử dụng cùng một phương pháp cho tất cả các phiên bản Android.
Xuất các tệp không phải phương tiện sang một thiết bị Sử dụng một phương pháp nếu ứng dụng của bạn sử dụng bộ nhớ có giới hạn. Sử dụng một phương pháp khác nếu ứng dụng của bạn chọn không sử dụng bộ nhớ có giới hạn.

Mở một tệp tài liệu

Sử dụng ý định ACTION_OPEN_DOCUMENT để yêu cầu người dùng chọn một tệp cần mở bằng bộ chọn hệ thống. Nếu muốn lọc các loại tệp mà bộ chọn hệ thống sẽ hiển thị để người dùng chọn, bạn có thể sử dụng setType() hoặc EXTRA_MIME_TYPES.

Ví dụ: Bạn có thể tìm thấy tất cả các tệp PDF, ODT và TXT bằng mã sau:

Kotlin

startActivityForResult(
        Intent(Intent.ACTION_OPEN_DOCUMENT).apply {
            addCategory(Intent.CATEGORY_OPENABLE)
            type = "*/*"
            putExtra(Intent.EXTRA_MIME_TYPES, arrayOf(
                    "application/pdf", // .pdf
                    "application/vnd.oasis.opendocument.text", // .odt
                    "text/plain" // .txt
            ))
        },
        REQUEST_CODE
      )

Java

Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
        intent.addCategory(Intent.CATEGORY_OPENABLE);
        intent.setType("*/*");
        intent.putExtra(Intent.EXTRA_MIME_TYPES, new String[] {
                "application/pdf", // .pdf
                "application/vnd.oasis.opendocument.text", // .odt
                "text/plain" // .txt
        });
        startActivityForResult(intent, REQUEST_CODE);

Ghi vào các tệp trên các phương tiện bộ nhớ phụ

Phương tiện bộ nhớ phụ gồm cả thẻ SD. Bạn có thể truy cập thông tin về phương tiện bộ nhớ nhất định bằng cách sử dụng lớp StorageVolume.

Kết hợp logic dựa trên phiên bản Android mà ứng dụng của bạn chạy.

Chạy trên Android 11

Sử dụng phương pháp sau:

  1. Sử dụng mô hình bộ nhớ có giới hạn.
  2. Nhắm mục tiêu đến Android 10 (API cấp 29) trở xuống.
  3. Khai báo quyền WRITE_EXTERNAL_STORAGE.
  4. Thực hiện một trong các loại quyền truy cập sau:
    • Quyền truy cập vào tệp bằng API MediaStore.
    • Quyền truy cập vào đường dẫn tệp trực tiếp bằng các API, chẳng hạn như File hoặc fopen().

Chạy trên các phiên bản cũ hơn

Sử dụng Khung truy cập bộ nhớ (Storage Access Framework), cho phép người dùng chọn vị trí trên một phương tiện bộ nhớ phụ mà ứng dụng của bạn có thể ghi tệp.

Di chuyển các tệp hiện có từ một vị trí bộ nhớ cũ

Một thư mục được coi là vị trí bộ nhớ cũ nếu không phải là thư mục dành riêng cho ứng dụng hoặc thư mục dùng chung công khai. Nếu ứng dụng của bạn tạo hoặc sử dụng các tệp ở vị trí bộ nhớ cũ, thì bạn nên di chuyển các tệp của ứng dụng sang những vị trí mà bộ nhớ có giới hạn có thể truy cập và thực hiện mọi thay đổi cần thiết để ứng dụng tương tác với các tệp trong bộ nhớ có giới hạn.

Duy trì quyền truy cập vào vị trí bộ nhớ cũ để di chuyển dữ liệu

Ứng dụng của bạn cần duy trì quyền truy cập vào vị trí bộ nhớ cũ để di chuyển mọi tệp ứng dụng sang những vị trí mà bộ nhớ có giới hạn có thể truy cập được. Phương pháp mà bạn nên sử dụng phụ thuộc vào cấp độ API mục tiêu của ứng dụng.

Nếu ứng dụng của bạn nhắm mục tiêu Android 11
  1. Đặt cờ preserveLegacyExternalStorage thành true nhằm duy trì mô hình bộ nhớ cũ để ứng dụng của bạn có thể di chuyển dữ liệu của người dùng khi họ nâng cấp lên phiên bản ứng dụng mới nhắm mục tiêu đến Android 11.

  2. Tiếp tục chọn không sử dụng bộ nhớ có giới hạn để ứng dụng có thể tiếp tục truy cập vào các tệp của bạn ở vị trí bộ nhớ cũ trên các thiết bị Android 10.

Nếu ứng dụng của bạn nhắm mục tiêu đến Android 10

Chọn không sử dụng bộ nhớ có giới hạn để giúp dễ dàng duy trì hành vi của ứng dụng trên các phiên bản Android.

Di chuyển dữ liệu ứng dụng

Khi ứng dụng của bạn đã sẵn sàng di chuyển, hãy sử dụng phương pháp sau:

  1. Nhắm mục tiêu đến Android 10 trở xuống.
  2. Chọn không sử dụng bộ nhớ có giới hạn để ứng dụng của bạn có quyền truy cập vào các tệp mà bạn cần di chuyển.
  3. Triển khai mã sử dụng API File để di chuyển các tệp từ vị trí hiện tại của những tệp đó trong /sdcard/ sang một vị trí có thể truy cập được bằng bộ nhớ có giới hạn:

    1. Di chuyển mọi tệp ứng dụng riêng tư sang thư mục mà phương thức getExternalFilesDir() trả về.
    2. Di chuyển mọi tệp không phải phương tiện đã chia sẻ sang một thư mục con dành riêng cho ứng dụng của thư mục Downloads/.
  4. Xoá các thư mục bộ nhớ cũ của ứng dụng khỏi thư mục /sdcard/.

Sau khi người dùng cài đặt phiên bản mới của ứng dụng, họ sẽ hoàn tất quá trình di chuyển dữ liệu trên thiết bị của mình. Bạn có thể theo dõi quá trình di chuyển trong cơ sở người dùng bằng cách tạo một sự kiện (event) phân tích.

Khi người dùng đã di chuyển dữ liệu của họ, phát hành một bản cập nhật khác cho ứng dụng của bạn, trong ứng dụng này bạn nhắm mục tiêu đến Android 11.

Chia sẻ nội dung với các ứng dụng khác

Để chia sẻ các tệp của ứng dụng với một ứng dụng khác, hãy sử dụng FileProvider. Đối với các ứng dụng cần chia sẻ tệp với nhau, bạn nên sử dụng nhà cung cấp nội dung cho từng ứng dụng, sau đó đồng bộ hoá dữ liệu khi các ứng dụng được thêm vào bộ sưu tập.

Lưu các tệp không phải phương tiện vào bộ nhớ đệm

Phương thức bạn nên sử dụng phụ thuộc vào loại tệp bạn cần lưu vào bộ nhớ đệm.

Xuất các tệp không phải phương tiện sang một thiết bị

Xác định vị trí mặc định thích hợp để lưu trữ các tệp không phải phương tiện. Cho phép người dùng xuất tệp từ thư mục dành riêng cho ứng dụng sang vị trí thường dễ truy cập hơn. Sử dụng các tệp đã tải xuống hoặc bộ sưu tập tài liệu của MediaStore để xuất các tệp không phải phương tiện sang thiết bị.

Xử lý tệp dành riêng cho ứng dụng

Trong trường hợp ứng dụng của bạn tạo các tệp mà những ứng dụng khác không cần truy cập hoặc không được truy cập, bạn có thể lưu trữ các tệp này trong vị trí bộ nhớ dành riêng cho ứng dụng.

Thư mục bộ nhớ trong

Hệ thống sẽ ngăn các ứng dụng khác truy cập vào những vị trí này, và trên Android 10 (API cấp 29) trở lên, những vị trí này sẽ được mã hoá. Những vị trí này là nơi phù hợp để lưu trữ dữ liệu nhạy cảm mà chỉ ứng dụng của bạn mới có thể truy cập.

Thư mục bộ nhớ ngoài

Nếu bộ nhớ trong không có đủ dung lượng để lưu trữ tệp dành riêng cho ứng dụng, hãy cân nhắc dùng bộ nhớ ngoài. Mặc dù một ứng dụng khác có thể truy cập vào các thư mục này nếu ứng dụng đó có các quyền thích hợp, nhưng các tệp được lưu trữ trong các thư mục này chỉ dành cho ứng dụng của bạn sử dụng.

Trên Android 4.4 (API cấp 19) trở lên, ứng dụng của bạn không cần yêu cầu quyền liên quan đến bộ nhớ để truy cập vào các thư mục dành riêng cho ứng dụng trong bộ nhớ ngoài.

Khi người dùng gỡ cài đặt ứng dụng của bạn, các tệp lưu trong bộ nhớ dành riêng cho ứng dụng sẽ bị xoá. Do đó, bạn không nên sử dụng bộ nhớ này để lưu bất kỳ nội dung nào mà người dùng muốn lưu trữ độc lập với ứng dụng của bạn.

Tạm thời chọn không sử dụng bộ nhớ có giới hạn

Trước khi ứng dụng của bạn hoàn toàn tương thích với bộ nhớ có giới hạn, bạn có thể tạm thời chọn không sử dụng, cả trong các kiểm thử và trong ứng dụng phát hành chính thức của bạn.

Chọn không tham gia trong kiểm thử

Trên Android 10 (API cấp 29) trở lên, theo mặc định, kiểm thử của ứng dụng sẽ chạy trong hộp cát bộ nhớ. Hộp cát này ngăn ứng dụng truy cập vào các tệp bên ngoài thư mục dành riêng cho ứng dụng và các thư mục được chia sẻ công khai.

Nếu một kiểm thử xuất ra các tệp cho máy chủ lưu trữ – chẳng hạn như ảnh chụp màn hình, dữ liệu gỡ lỗi, dữ liệu về độ bao phủ hoặc chỉ số về hiệu suất, thì bạn có thể ghi các tệp này vào thư mục chung. Để làm như vậy, hãy thêm cờ sau đây vào phần khai thác liên quan gọi am instrument:

-e no-isolated-storage 1

Cờ này ảnh hưởng đến tất cả hành vi của trường hợp kiểm thử được đo lường và cũng ảnh hưởng đến tất cả các mã kiểm thử được gọi. Do đó, khi sử dụng cờ này, bạn không thể xác thực khả năng tương thích của ứng dụng với bộ nhớ có giới hạn. Đối với đầu ra kiểm thử, tốt hơn là bạn nên ghi vào bộ nhớ có giới hạn trong ứng dụng mà lệnh sell có thể đọc được. Sau đó, bạn có thể kéo (pull) thư mục có giới hạn trong ứng dụng đó. Để xác định thư mục mà từ đó bạn muốn kéo, hãy gọi getExternalMediaDirs().

Chọn không sử dụng trong ứng dụng phát hành chính thức

Nếu ứng dụng của bạn nhắm mục tiêu đến Android 10 (API cấp 29) trở xuống, bạn có thể tạm thời chọn không sử dụng bộ nhớ có giới hạn trong ứng dụng phát hành chính thức. Tuy nhiên, nếu nhắm mục tiêu đến Android 10, bạn cần đặt giá trị của requestLegacyExternalStorage thành true trong tệp kê khai của ứng dụng:

<manifest ... >
  <!-- This attribute is "false" by default on apps targeting
       Android 10. -->
  <application android:requestLegacyExternalStorage="true" ... >
    ...
  </application>
</manifest>

Để kiểm thử cách một ứng dụng nhắm mục tiêu đến Android 10 trở xuống hoạt động khi sử dụng bộ nhớ có giới hạn, bạn có thể chọn tham gia hành vi bằng cách đặt giá trị requestLegacyExternalStorage thành false. Nếu đang kiểm thử trên một thiết bị chạy Android 11, bạn cũng có thể sử dụng cờ tương thích của ứng dụng để kiểm thử hành vi của ứng dụng có hoặc không có bộ nhớ có giới hạn.

Tài nguyên khác

Để biết thêm thông tin về bộ nhớ Android, hãy xem các tài liệu sau:

Bài đăng trên blog