Detectar el tamaño del montón de aplicaciones en Android
¿Cómo se detecta mediante programación el tamaño del montón de aplicaciones disponible para una aplicación de Android?
Escuché que hay una función que hace esto en versiones posteriores del SDK. En cualquier caso, estoy buscando una solución que funcione para 1.5 y versiones posteriores.
Hay dos formas de pensar en la frase "tamaño del montón de aplicaciones disponible":
¿Cuánto montón puede usar mi aplicación antes de que se active un error grave? Y
¿Cuánto montón debería usar mi aplicación, dadas las limitaciones de la versión del sistema operativo Android y el hardware del dispositivo del usuario?
Existe un método diferente para determinar cada uno de los anteriores.
Para el punto 1 anterior:maxMemory()
que se puede invocar (por ejemplo, en el onCreate()
método de su actividad principal) de la siguiente manera:
Runtime rt = Runtime.getRuntime();
long maxMemory = rt.maxMemory();
Log.v("onCreate", "maxMemory:" + Long.toString(maxMemory));
Este método le indica cuántos bytes totales de montón puede usar su aplicación.
Para el punto 2 anterior:getMemoryClass()
que se puede invocar de la siguiente manera:
ActivityManager am = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
int memoryClass = am.getMemoryClass();
Log.v("onCreate", "memoryClass:" + Integer.toString(memoryClass));
Este método le indica aproximadamente cuántos megabytes de montón debe usar su aplicación si quiere respetar adecuadamente los límites del dispositivo actual y los derechos de otras aplicaciones para ejecutarse sin ser forzadas repetidamente al ciclo onStop()
/ onResume()
como lo hacen de manera grosera. se queda sin memoria mientras tu elefantina aplicación se baña en el jacuzzi de Android.
Esta distinción no está claramente documentada, hasta donde yo sé, pero he probado esta hipótesis en cinco dispositivos Android diferentes (ver más abajo) y he confirmado, a mi propia satisfacción, que se trata de una interpretación correcta.
Para una versión estándar de Android, maxMemory()
normalmente devolverá aproximadamente la misma cantidad de megabytes que se indican en getMemoryClass()
(es decir, aproximadamente un millón de veces el último valor).
La única situación (que conozco) en la que los dos métodos pueden divergir es en un dispositivo rooteado que ejecuta una versión de Android como CyanogenMod, que permite al usuario seleccionar manualmente qué tamaño de montón debe permitirse para cada aplicación. En CM, por ejemplo, esta opción aparece en "Configuración de CyanogenMod"/"Rendimiento"/"Tamaño del montón de VM".
NOTA: TENGA EN CUENTA QUE CONFIGURAR ESTE VALOR MANUALMENTE PUEDE ERRAR SU SISTEMA, ESPECIALMENTE si selecciona un valor menor al normal para su dispositivo.
Aquí están los resultados de mi prueba que muestran los valores devueltos por maxMemory()
y getMemoryClass()
para cuatro dispositivos diferentes que ejecutan CyanogenMod, usando dos valores de montón diferentes (configurados manualmente) para cada uno:
- G1:
- Con el tamaño del montón de VM configurado en 16 MB:
- memoria máxima: 16777216
- getMemoryClass: 16
- Con el tamaño del montón de VM configurado en 24 MB:
- memoria máxima: 25165824
- getMemoryClass: 16
- Con el tamaño del montón de VM configurado en 16 MB:
- Motodroide:
- Con el tamaño del montón de VM configurado en 24 MB:
- memoria máxima: 25165824
- getMemoryClass: 24
- Con el tamaño del montón de VM configurado en 16 MB:
- memoria máxima: 16777216
- getMemoryClass: 24
- Con el tamaño del montón de VM configurado en 24 MB:
- Nexo Uno:
- Con el tamaño del montón de VM establecido en 32 MB:
- memoria máxima: 33554432
- getMemoryClass: 32
- Con el tamaño del montón de VM configurado en 24 MB:
- memoria máxima: 25165824
- getMemoryClass: 32
- Con el tamaño del montón de VM establecido en 32 MB:
- Viewsonic GTab:
- Con el tamaño del montón de VM establecido en 32:
- memoria máxima: 33554432
- getMemoryClass: 32
- Con el tamaño del montón de VM establecido en 64:
- memoria máxima: 67108864
- getMemoryClass: 32
- Con el tamaño del montón de VM establecido en 32:
Además de lo anterior, probé en una tableta Novo7 Paladin con Ice Cream Sandwich. Esta era esencialmente una versión estándar de ICS, excepto que rooteé la tableta mediante un proceso simple que no reemplaza todo el sistema operativo y, en particular, no proporciona una interfaz que permita ajustar manualmente el tamaño del montón.
Para ese dispositivo, aquí están los resultados:
- Novo7
- memoria máxima: 62914560
- getMemoryClass: 60
Además (según Kishore en un comentario a continuación):
- HTC uno X
- memoria máxima: 67108864
- getMemoryClass: 64
Y (según el comentario de akauppi):
- Samsung Galaxy Core Plus
- maxMemory: (No especificado en el comentario)
- getMemoryClass: 48
- grandeMemoryClass: 128
Según un comentario de cmcromance:
- Montón grande del Galaxy S3 (Jelly Bean)
- memoria máxima: 268435456
- getMemoryClass: 64
Y (comentarios del porcentaje):
- LG Nexus 5 (4.4.3) normal
- memoria máxima: 201326592
- getMemoryClass: 192
- Montón grande LG Nexus 5 (4.4.3)
- memoria máxima: 536870912
- getMemoryClass: 192
- Galaxy Nexus (4.3) normal
- memoria máxima: 100663296
- getMemoryClass: 96
- Gran montón de Galaxy Nexus (4.3)
- memoria máxima: 268435456
- getMemoryClass: 96
- Galaxy S4 Edición Play Store (4.4.2) normal
- memoria máxima: 201326592
- getMemoryClass: 192
- Montón grande del Galaxy S4 Play Store Edition (4.4.2)
- memoria máxima: 536870912
- getMemoryClass: 192
Otros dispositivos
- Huawei Nexus 6P (6.0.1) normal
- memoria máxima: 201326592
- getMemoryClass: 192
No he probado estos dos métodos usando la opción de manifiesto especial android:largeHeap="true" disponible desde Honeycomb, pero gracias a cmcromance y tencent tenemos algunos valores de muestra de bigHeap, como se informó anteriormente.
Mi expectativa (que parece estar respaldada por los grandes números de montón anteriores) sería que esta opción tendría un efecto similar a configurar el montón manualmente a través de un sistema operativo rooteado, es decir, aumentaría el valor de maxMemory()
sin getMemoryClass()
tocarlo. Hay otro método, getLargeMemoryClass(), que indica cuánta memoria está permitida para una aplicación que utiliza la configuración largeHeap. La documentación para getLargeMemoryClass() indica que "la mayoría de las aplicaciones no deberían necesitar esta cantidad de memoria y, en cambio, deberían permanecer con el límite de getMemoryClass()".
Si he adivinado correctamente, entonces usar esa opción tendría los mismos beneficios (y peligros) que usar el espacio disponible por un usuario que ha aumentado el montón a través de un sistema operativo rooteado (es decir, si su aplicación usa memoria adicional, probablemente no funcione tan bien con otras aplicaciones que el usuario esté ejecutando al mismo tiempo).
Tenga en cuenta que aparentemente la clase de memoria no necesita ser múltiplo de 8 MB.
Podemos ver en lo anterior que el getMemoryClass()
resultado no cambia para una determinada configuración de dispositivo/SO, mientras que el valor de maxMemory() cambia cuando el usuario configura el montón de manera diferente.
Mi propia experiencia práctica es que en el G1 (que tiene una clase de memoria de 16), si selecciono manualmente 24 MB como tamaño de montón, puedo ejecutar sin errores incluso cuando se permite que mi uso de memoria aumente hasta 20 MB (presumiblemente podría llegar hasta 24 MB, aunque no lo he probado). Pero otras aplicaciones de tamaño similar pueden borrarse de la memoria como resultado de la capacidad de mi propia aplicación. Y, a la inversa, mi aplicación puede borrarse de la memoria si el usuario pone en primer plano estas otras aplicaciones de alto mantenimiento.
Por lo tanto, no puede exceder la cantidad de memoria especificada por maxMemory()
. Y debe intentar mantenerse dentro de los límites especificados por getMemoryClass()
. Una forma de hacerlo, si todo lo demás falla, podría ser limitar la funcionalidad de dichos dispositivos de manera que se conserve la memoria.
Finalmente, si planea superar la cantidad de megabytes especificada en getMemoryClass()
, mi consejo sería trabajar mucho y duro para guardar y restaurar el estado de su aplicación, de modo que la experiencia del usuario sea prácticamente ininterrumpida si ocurre un ciclo onStop()
/ onResume()
.
En mi caso, por razones de rendimiento, estoy limitando mi aplicación a dispositivos que ejecutan 2.2 y superiores, y eso significa que casi todos los dispositivos que ejecutan mi aplicación tendrán una clase de memoria de 24 o superior. Por lo tanto, puedo diseñar para ocupar hasta 20 MB de almacenamiento dinámico y estar bastante seguro de que mi aplicación funcionará bien con las otras aplicaciones que el usuario pueda estar ejecutando al mismo tiempo.
Pero siempre habrá algunos usuarios rooteados que hayan cargado una versión 2.2 o superior de Android en un dispositivo más antiguo (por ejemplo, un G1). Cuando se encuentre con una configuración de este tipo, idealmente debería reducir el uso de memoria, incluso si maxMemory()
le indica que puede ir mucho más allá de los 16 MB a los que getMemoryClass()
debe apuntar . Y si no puede garantizar de manera confiable que su aplicación se ajuste a ese presupuesto, al menos asegúrese de que onStop()
/ onResume()
funcione a la perfección.
getMemoryClass()
, como lo indicó Diane Hackborn (hackbod) anteriormente, solo está disponible en el nivel API 5 (Android 2.0) y, por lo tanto, como ella aconseja, puede asumir que el hardware físico de cualquier dispositivo que ejecute una versión anterior del sistema operativo está diseñado. para admitir de manera óptima aplicaciones que ocupan un espacio de almacenamiento dinámico de no más de 16 MB.
Por el contrario, maxMemory()
, según la documentación, está disponible desde el nivel API 1. maxMemory()
, en una versión anterior a la 2.0, probablemente devolverá un valor de 16 MB, pero veo que en mis versiones de CyanogenMod (mucho posteriores) el usuario Puede seleccionar un valor de almacenamiento dinámico tan bajo como 12 MB, lo que presumiblemente daría como resultado un límite de almacenamiento dinámico más bajo, por lo que sugeriría que continúe probando el maxMemory()
valor, incluso para versiones del sistema operativo anteriores a la 2.0. Es posible que incluso tenga que negarse a ejecutar en el improbable caso de que este valor esté establecido incluso por debajo de 16 MB, si necesita tener más de lo que maxMemory()
indica que está permitido.
La API oficial es:
Esto se introdujo en 2.0, donde aparecieron dispositivos de memoria más grandes. Puede suponer que los dispositivos que ejecutan versiones anteriores del sistema operativo utilizan la clase de memoria original (16).
Así es como lo haces:
Obtener el tamaño de montón máximo que la aplicación puede usar:
Runtime runtime = Runtime.getRuntime();
long maxMemory=runtime.maxMemory();
Obtener cuánto del montón utiliza actualmente su aplicación:
long usedMemory=runtime.totalMemory() - runtime.freeMemory();
Obtener cuánto del montón puede usar ahora su aplicación (memoria disponible):
long availableMemory=maxMemory-usedMemory;
Y, para formatear bien cada uno de ellos, puedes usar:
String formattedMemorySize=Formatter.formatShortFileSize(context,memorySize);