Jest koniec 2018 roku, więc wszystko się zmieniło.
Po pierwsze: uruchom aplikację i otwórz zakładkę Android Profiler w Android Studio. Zobaczysz, ile pamięci zużywa, będziesz zaskoczony, ale może przydzielić dużo pamięci RAM.
Również tutaj jest wielki artykuł w oficjalnych docs szczegółowe instrukcje na temat korzystania z pamięci Profiler, które może dać ci dogłębnie swojego zarządzania pamięcią.
Ale w większości przypadków wystarczy Ci zwykły Android Profiler.
Zwykle aplikacja zaczyna się od alokacji 50 MB pamięci RAM, ale natychmiast przeskakuje do 90 MB po rozpoczęciu ładowania niektórych zdjęć do pamięci. Po otwarciu Aktywności za pomocą ViewPagera z fabrycznie załadowanymi zdjęciami (po 3,5 MB każdy) możesz łatwo uzyskać 190 MB w kilka sekund.
Ale to nie znaczy, że masz problemy z zarządzaniem pamięcią.
Najlepsza rada, jaką mogę dać, to postępować zgodnie ze wskazówkami i najlepszymi praktykami, używać najlepszych bibliotek do ładowania obrazów (Glide, Picasso) i wszystko będzie dobrze.
Ale jeśli chcesz coś dostosować i naprawdę potrzebujesz wiedzieć, ile pamięci możesz przydzielić ręcznie, możesz uzyskać całkowitą wolną pamięć i obliczyć z góry określoną część (w%). W moim przypadku potrzebowałem buforować odszyfrowane zdjęcia w pamięci, więc nie muszę ich odszyfrowywać za każdym razem, gdy użytkownik przesuwa się po liście.
W tym celu możesz skorzystać z gotowej do użycia klasy LruCache . Jest to klasa pamięci podręcznej, która automatycznie śledzi ilość pamięci przydzielonej przez obiekty (lub liczbę instancji) i usuwa najstarsze, aby zachować najnowsze według historii użycia.
Oto świetny samouczek, jak go używać.
W moim przypadku utworzyłem 2 instancje pamięci podręcznych: dla kciuków i załączników. Ustawiono je jako statyczne dzięki dostępowi singleton, dzięki czemu są dostępne globalnie w całej aplikacji.
klasa pamięci podręcznej:
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;
}
}
Oto jak obliczam dostępną wolną pamięć RAM i ile mogę z niej ugryźć:
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);
}
I tak używam go w adapterach, aby uzyskać buforowany obraz:
byte[] decryptedThumbnail = BitmapLruCache.getDecryptedThumbCacheInstance().getBitmap(thumbUri);
i jak ustawiłem go w pamięci podręcznej w wątku w tle (zwykłe AsyncTask):
BitmapLruCache.getDecryptedThumbCacheInstance().addBitmap(thumbUri, thumbBytes);
Moja aplikacja jest ukierunkowana na API 19+, więc urządzenia nie są stare, a te części dostępnej pamięci RAM są wystarczająco dobre dla pamięci podręcznej w moim przypadku (1% i 3%).
Ciekawostka: Android nie ma żadnych interfejsów API ani innych hacków, aby uzyskać ilość pamięci przydzielonej do Twojej aplikacji, jest obliczana na bieżąco na podstawie różnych czynników.
PS Używam statycznego pola klasy do przechowywania pamięci podręcznej, ale zgodnie z najnowszymi wytycznymi Androida zaleca się użycie do tego celu komponentu architektury ViewModel .