Es ist Ende 2018, also haben sich die Dinge geändert.
Zunächst einmal: Führen Sie Ihre App aus und öffnen Sie die Registerkarte Android Profiler in Android Studio. Sie werden sehen, wie viel Speicher es verbraucht, Sie werden überrascht sein, aber es kann viel RAM zuweisen.
Außerdem finden Sie hier einen großartigen Artikel in offiziellen Dokumenten mit detaillierten Anweisungen zur Verwendung von Memory Profiler, mit denen Sie einen detaillierten Einblick in Ihre Speicherverwaltung erhalten.
In den meisten Fällen reicht Ihnen jedoch Ihr regulärer Android Profiler.
Normalerweise startet eine App mit 50 MB RAM-Zuweisung, springt jedoch sofort auf 90 MB, wenn Sie einige Fotos in den Speicher laden. Wenn Sie Activity mit einem ViewPager mit vorinstallierten Fotos (jeweils 3,5 MB) öffnen, erhalten Sie in Sekundenschnelle 190 MB.
Dies bedeutet jedoch nicht, dass Sie Probleme mit der Speicherverwaltung haben.
Der beste Rat, den ich geben kann, ist, die Richtlinien und Best Practices zu befolgen, Top-Bibliotheken zum Laden von Bildern zu verwenden (Glide, Picasso) und alles in Ordnung zu sein.
Wenn Sie jedoch etwas anpassen müssen und wirklich wissen müssen, wie viel Speicher Sie manuell zuweisen können, können Sie den gesamten freien Speicher abrufen und einen festgelegten Anteil (in%) daraus berechnen. In meinem Fall musste ich entschlüsselte Fotos im Speicher zwischenspeichern, damit ich sie nicht jedes Mal entschlüsseln muss, wenn der Benutzer durch die Liste gleitet.
Zu diesem Zweck können Sie die gebrauchsfertige LruCache-Klasse verwenden . Es handelt sich um eine Cache-Klasse, die automatisch nachverfolgt, wie viel Speicher Ihre Objekte zuweisen (oder wie viele Instanzen), und die älteste entfernt, um die aktuelle anhand ihres Verwendungsverlaufs zu erhalten.
Hier ist ein großartiges Tutorial zur Verwendung.
In meinem Fall habe ich zwei Instanzen von Caches erstellt: für Daumen und Anhänge. Sie wurden mit Singleton-Zugriff statisch gemacht, sodass sie global in der gesamten App verfügbar sind.
Cache-Klasse:
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;
}
}
So berechne ich den verfügbaren freien RAM und wie viel kann ich daraus herausbeißen:
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);
}
Und so verwende ich es in Adaptern, um zwischengespeicherte Bilder zu erhalten:
byte[] decryptedThumbnail = BitmapLruCache.getDecryptedThumbCacheInstance().getBitmap(thumbUri);
und wie ich es im Hintergrund-Thread in den Cache setze (reguläre AsyncTask):
BitmapLruCache.getDecryptedThumbCacheInstance().addBitmap(thumbUri, thumbBytes);
Meine App zielt auf API 19+ ab, sodass Geräte nicht alt sind und diese Teile des verfügbaren Arbeitsspeichers in meinem Fall für den Cache ausreichen (1% und 3%).
Unterhaltsame Tatsache: Android verfügt über keine APIs oder andere Hacks, um die Ihrer App zugewiesene Speichermenge abzurufen. Sie wird im laufenden Betrieb anhand verschiedener Faktoren berechnet.
PS Ich verwende ein statisches Klassenfeld, um einen Cache zu speichern, aber gemäß den neuesten Android-Richtlinien wird empfohlen, die ViewModel-Architekturkomponente für diesen Zweck zu verwenden.