C'est la fin de 2018 donc les choses ont changé.
Tout d'abord: exécutez votre application et ouvrez l'onglet Android Profiler dans Android Studio. Vous verrez combien de mémoire il consomme, vous serez surpris mais il peut allouer beaucoup de RAM.
Voici également un excellent article dans la documentation officielle avec des instructions détaillées sur l'utilisation de Memory Profiler qui peuvent vous donner un aperçu détaillé de la gestion de votre mémoire.
Mais dans la plupart des cas, votre profileur Android régulier vous suffira.
Habituellement, une application commence avec 50 Mo d'allocation de RAM, mais passe instantanément à 90 Mo lorsque vous commencez à charger des photos en mémoire. Lorsque vous ouvrez Activity avec un ViewPager avec des photos préchargées (3,5 Mo chacune), vous pouvez facilement obtenir 190 Mo en quelques secondes.
Mais cela ne signifie pas que vous avez des problèmes avec la 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 d'images (Glide, Picasso) et tout ira bien.
Mais si vous avez besoin de personnaliser quelque chose et que vous avez vraiment besoin de savoir combien de mémoire vous pouvez allouer manuellement, vous pouvez obtenir la mémoire totale disponible et en calculer une partie prédéterminée (en%). Dans mon cas, j'avais besoin de mettre en cache les photos déchiffrées en mémoire afin de ne pas avoir besoin de les déchiffrer à chaque fois que l'utilisateur glisse dans la liste.
Pour cela, vous pouvez utiliser la classe LruCache prête à l'emploi . C'est une classe de cache qui suit automatiquement la quantité de mémoire allouée par vos objets (ou le nombre d'instances) et supprime la plus ancienne pour conserver la plus récente par 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 pouces et les pièces jointes. Rendez-les statiques avec un accès unique afin qu'ils soient disponibles dans le monde entier 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;
}
}
Voici comment je calcule la RAM libre disponible et combien 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 Adapters pour obtenir une image en cache:
byte[] decryptedThumbnail = BitmapLruCache.getDecryptedThumbCacheInstance().getBitmap(thumbUri);
et comment je l'ai mis en cache dans le thread d'arrière-plan (AsyncTask normal):
BitmapLruCache.getDecryptedThumbCacheInstance().addBitmap(thumbUri, thumbBytes);
Mon application cible l'API 19+, donc les appareils ne sont pas vieux et ces portions de RAM disponible sont assez bonnes pour le cache dans mon cas (1% et 3%).
Fait amusant: Android ne possède pas d'API ou d'autres hacks pour obtenir la quantité de mémoire allouée à votre application, elle est calculée à la volée en fonction de divers facteurs.
PS J'utilise un champ de classe statique pour contenir un cache, mais selon les dernières directives Android, il est recommandé d'utiliser le composant d'architecture ViewModel à cette fin.