Es finales de 2018, por lo que las cosas han cambiado.
En primer lugar: ejecute su aplicación y abra la pestaña Android Profiler en Android Studio. Verá cuánta memoria consume, se sorprenderá pero puede asignar mucha RAM.
También aquí hay un gran artículo en documentos oficiales con instrucciones detalladas sobre cómo usar Memory Profiler que puede darle una visión en profundidad de su administración de memoria.
Pero en la mayoría de los casos, su Android Profiler habitual será suficiente para usted.
Por lo general, una aplicación comienza con 50Mb de asignación de RAM, pero salta instantáneamente hasta 90Mb cuando comienza a cargar algunas fotos en la memoria. Cuando abres Activity con un ViewPager con fotos precargadas (3,5Mb cada una) puedes obtener 190Mb fácilmente en segundos.
Pero esto no significa que tenga problemas con la administración de la memoria.
El mejor consejo que puedo dar es seguir las pautas y las mejores prácticas, usar las mejores bibliotecas para cargar imágenes (Glide, Picasso) y estarás bien.
Pero si necesita adaptar algo y realmente necesita saber cuánta memoria puede asignar manualmente, puede obtener memoria libre total y calcular una porción predeterminada (en%) de ella. En mi caso, necesitaba almacenar en caché las fotos descifradas en la memoria, así que no necesito descifrarlas cada vez que el usuario se desliza por la lista.
Para este propósito, puede usar la clase LruCache lista para usar . Es una clase de caché que rastrea automáticamente la cantidad de memoria que asignan sus objetos (o la cantidad de instancias) y elimina la más antigua para mantener la reciente por su historial de uso.
Aquí hay un gran tutorial sobre cómo usarlo.
En mi caso, creé 2 instancias de cachés: para pulgares y archivos adjuntos. Los hizo estáticos con acceso singleton para que estén disponibles globalmente en toda la aplicación.
clase de caché:
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;
}
}
Así es como calculo la RAM libre disponible y cuánto puedo extraer:
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);
}
Y así es como lo uso en Adaptadores para obtener la imagen en caché:
byte[] decryptedThumbnail = BitmapLruCache.getDecryptedThumbCacheInstance().getBitmap(thumbUri);
y cómo lo configuré en caché en el hilo de fondo (AsyncTask regular):
BitmapLruCache.getDecryptedThumbCacheInstance().addBitmap(thumbUri, thumbBytes);
Mi aplicación está dirigida a API 19+, por lo que los dispositivos no son viejos y estas porciones de RAM disponibles son lo suficientemente buenas para la caché en mi caso (1% y 3%).
Dato curioso: Android no tiene ninguna API u otros hacks para obtener la cantidad de memoria asignada a su aplicación, se calcula sobre la marcha en función de varios factores.
PD: Estoy usando un campo de clase estático para contener un caché, pero según las últimas directrices de Android, se recomienda usar el componente de arquitectura ViewModel para ese propósito.