Nous sommes à la fin de l'année 2018, les choses ont donc changé.
Tout d'abord, lancez votre application et ouvrez l'onglet Android Profiler dans Android Studio. Vous verrez combien de mémoire elle consomme, vous serez surpris mais elle peut allouer beaucoup de RAM.
Aussi Voici un excellent article dans les docs officiels avec des instructions détaillées sur la façon d'utiliser le Memory Profiler qui peut vous donner un aperçu en profondeur de votre gestion de la mémoire.
Mais dans la plupart des cas, votre Android Profiler habituel sera suffisant pour vous.
En général, une application commence avec une allocation de 50 Mo de RAM, mais elle passe instantanément à 90 Mo lorsque vous commencez à charger des photos en mémoire. Lorsque vous ouvrez Activity avec un ViewPager contenant des photos préchargées (3,5 Mo chacune), vous pouvez obtenir 190 Mo en quelques secondes.
Mais cela ne signifie pas que vous avez des problèmes de gestion de la mémoire.
Le meilleur conseil que je puisse donner est de suivre les directives et les meilleures pratiques, d'utiliser les meilleures bibliothèques pour le chargement des images (Glide, Picasso) et tout ira bien.
Mais si vous devez adapter quelque chose et que vous avez vraiment besoin de savoir combien de mémoire vous pouvez allouer manuellement, vous pouvez obtenir la mémoire libre totale et calculer une portion prédéterminée (en %) de celle-ci. Dans mon cas, j'avais besoin de mettre en cache les photos décryptées en mémoire pour ne pas avoir à les décrypter à chaque fois que l'utilisateur glisse dans la liste.
Pour ce faire, vous pouvez utiliser des produits prêts à l'emploi Classe LruCache . Il s'agit d'une classe de cache qui suit automatiquement la quantité de mémoire allouée par vos objets (ou le nombre d'instances) et supprime les plus anciens pour conserver les plus récents en fonction de leur historique d'utilisation. Voici un excellent tutoriel sur la façon de l'utiliser.
Dans mon cas, j'ai créé 2 instances de caches : pour les vignettes et les pièces jointes. Je les ai rendues statiques avec un accès singleton pour qu'elles soient disponibles globalement dans toute l'application.
classe de cache :
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;
}
}
C'est ainsi que je calcule la RAM libre disponible et la quantité que je peux en tirer :
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);
}
Et voici comment je l'utilise dans les adaptateurs pour obtenir une image en cache :
byte[] decryptedThumbnail = BitmapLruCache.getDecryptedThumbCacheInstance().getBitmap(thumbUri);
et comment je le mets en cache dans le fil d'arrière-plan (AsyncTask ordinaire) :
BitmapLruCache.getDecryptedThumbCacheInstance().addBitmap(thumbUri, thumbBytes);
Mon application vise l'API 19+, les appareils ne sont donc pas vieux et ces portions de RAM disponible sont suffisantes pour le cache dans mon cas (1% et 3%).
Fait amusant : Android ne dispose pas d'API ou d'autres astuces pour obtenir la quantité de mémoire allouée à votre application, elle est calculée à la volée en fonction de divers facteurs.
P.S. J'utilise un champ de classe statique pour contenir un cache, mais selon les dernières directives Android, il est recommandé d'utiliser Composant de l'architecture ViewModel à cette fin.