Đã cuối năm 2018 nên mọi thứ đã thay đổi.
Trước hết: chạy ứng dụng của bạn và mở tab Android Profiler trong Android Studio. Bạn sẽ thấy nó tiêu thụ bao nhiêu bộ nhớ, bạn sẽ ngạc nhiên nhưng nó có thể phân bổ rất nhiều RAM.
Ngoài ra đây là một bài viết tuyệt vời trong các tài liệu chính thức với các hướng dẫn chi tiết về cách sử dụng Memory Profiler có thể cung cấp cho bạn cái nhìn sâu sắc về quản lý bộ nhớ của bạn.
Nhưng trong hầu hết các trường hợp, Android Profiler thông thường của bạn sẽ đủ cho bạn.
Thông thường, một ứng dụng bắt đầu với phân bổ RAM 50Mb nhưng ngay lập tức nhảy lên tới 90Mb khi bạn bắt đầu tải một số ảnh trong bộ nhớ. Khi bạn mở Activity bằng ViewPager với các ảnh được tải sẵn (mỗi ảnh 3,5Mb), bạn có thể nhận được 190Mb dễ dàng trong vài giây.
Nhưng điều này không có nghĩa là bạn có vấn đề với quản lý bộ nhớ.
Lời khuyên tốt nhất tôi có thể đưa ra là tuân theo các hướng dẫn và thực tiễn tốt nhất, sử dụng các thư viện hàng đầu để tải hình ảnh (Glide, Picasso) và bạn sẽ ổn.
Nhưng nếu bạn cần điều chỉnh một cái gì đó và bạn thực sự cần biết bạn có thể phân bổ bao nhiêu bộ nhớ theo cách thủ công, bạn có thể lấy tổng bộ nhớ trống và tính một phần được xác định trước (tính theo%) trong số đó. Trong trường hợp của tôi, tôi cần lưu trữ những bức ảnh được giải mã vào bộ nhớ vì vậy tôi không cần giải mã chúng mỗi khi người dùng trượt qua danh sách.
Đối với mục đích này, bạn có thể sử dụng sẵn sàng để sử dụng lớp LruCache . Đó là một lớp bộ đệm tự động theo dõi lượng bộ nhớ mà các đối tượng của bạn phân bổ (hoặc số lượng phiên bản) và loại bỏ cái cũ nhất để giữ gần đây theo lịch sử sử dụng của chúng.
Đây là một hướng dẫn tuyệt vời về cách sử dụng nó.
Trong trường hợp của tôi, tôi đã tạo 2 trường hợp lưu trữ: cho ngón tay cái và tệp đính kèm. Làm cho chúng tĩnh với quyền truy cập singleton để chúng có sẵn trên toàn cầu trong ứng dụng.
lớp bộ đệm:
public class BitmapLruCache extends LruCache<Uri, byte[]> {
private static final float CACHE_PART_FOR_THUMBS_PRC = 0.01f; // 1% (Nexus 5X - 5Mb)
private static final float CACHE_PART_FOR_ATTACHMENTS_PRC = 0.03f;// 3% (Nexus 5X - 16Mb)
private static BitmapLruCache thumbCacheInstance;
private static BitmapLruCache attachmentCacheInstance;
public static synchronized BitmapLruCache getDecryptedThumbCacheInstance() {
if (thumbCacheInstance == null) {
int cacheSize = getCacheSize(CACHE_PART_FOR_THUMBS_PRC);
//L.log("creating BitmapLruCache for Thumb with size: " + cacheSize + " bytes");
thumbCacheInstance = new BitmapLruCache(cacheSize);
return thumbCacheInstance;
} else {
return thumbCacheInstance;
}
}
public static synchronized BitmapLruCache getDecryptedAttachmentCacheInstance() {
if (attachmentCacheInstance == null) {
int cacheSize = getCacheSize(CACHE_PART_FOR_ATTACHMENTS_PRC);
// L.log("creating BitmapLruCache for Attachment with size: " + cacheSize + " bytes");
attachmentCacheInstance = new BitmapLruCache(cacheSize);
return attachmentCacheInstance;
} else {
return attachmentCacheInstance;
}
}
private BitmapLruCache(int maxSize) {
super(maxSize);
}
public void addBitmap(Uri uri, byte[] bitmapBytes) {
if (get(uri) == null && bitmapBytes != null)
put(uri, bitmapBytes);
}
public byte[] getBitmap(Uri uri) {
return get(uri);
}
@Override
protected int sizeOf(Uri uri, byte[] bitmapBytes) {
// The cache size will be measured in bytes rather than number of items.
return bitmapBytes.length;
}
}
Đây là cách tôi tính toán RAM miễn phí có sẵn và tôi có thể cắn được bao nhiêu:
private static int getCacheSize(float partOfTotalFreeMemoryToUseAsCache){
final long maxMemory = Runtime.getRuntime().maxMemory();
//Use ... of available memory for List Notes thumb cache
return (int) (maxMemory * partOfTotalFreeMemoryToUseAsCache);
}
Và đây là cách tôi sử dụng nó trong Bộ điều hợp để có được hình ảnh được lưu trong bộ nhớ cache:
byte[] decryptedThumbnail = BitmapLruCache.getDecryptedThumbCacheInstance().getBitmap(thumbUri);
và cách tôi đặt nó vào bộ đệm trong luồng nền (AsyncTask thông thường):
BitmapLruCache.getDecryptedThumbCacheInstance().addBitmap(thumbUri, thumbBytes);
Ứng dụng của tôi nhắm mục tiêu API 19+ để các thiết bị không cũ và các phần RAM có sẵn này đủ tốt để lưu vào bộ đệm trong trường hợp của tôi (1% và 3%).
Sự thật thú vị: Android không có bất kỳ API hoặc các bản hack nào khác để có được dung lượng bộ nhớ được phân bổ cho ứng dụng của bạn, nó được tính toán nhanh chóng dựa trên các yếu tố khác nhau.
PS Tôi đang sử dụng trường lớp tĩnh để giữ bộ đệm nhưng theo hướng dẫn mới nhất của Android, nên sử dụng thành phần kiến trúc ViewModel cho mục đích đó.