Pregunta

¿Cómo 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.

¿Fue útil?

Solución

Hay dos formas de pensar en su frase "tamaño de almacenamiento dinámico de aplicaciones disponible":

  1. ¿Cuánto montón puede usar mi aplicación antes de que se active un error grave?Y

  2. Cuánto montón debería ¿uso mi aplicación, dadas las restricciones 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 la actividad principal de onCreate() método) 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 totales bytes de montón es tu aplicación permitido para usar.

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 heap tu aplicación debería úselo si quiere respetar adecuadamente los límites del dispositivo actual y los derechos de otras aplicaciones para ejecutarse sin verse obligadas repetidamente a hacerlo. onStop() / onResume() ciclo a medida que se vacían bruscamente de la memoria mientras su aplicación elefantina 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 satisfacción que esta es una interpretación correcta.

Para una versión estándar de Android, maxMemory() normalmente devolverá aproximadamente el mismo número de megabytes que se indican en getMemoryClass() (es decir, aproximadamente un millón de veces el último valor).

La única situación (de la que tengo conocimiento) 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 seleccione qué tan grande debe permitirse un tamaño de almacenamiento dinámico para cada aplicación.En CM, por ejemplo, esta opción aparece en "Configuración de CyanogenMod" / "Rendimiento" / "Tamaño del montón de la máquina virtual".

NOTA:TENGA EN CUENTA QUE CONFIGURAR ESTE VALOR MANUALMENTE PUEDE ESTROPEAR SU SISTEMA, ESPECIALMENTE si selecciona un valor más pequeño de lo 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, utilizando dos valores de almacenamiento dinámico diferentes (establecidos manualmente) para cada uno:

  • G1:
    • Con el tamaño de almacenamiento dinámico de VM establecido en 16 MB:
      • Maxmemoria:16777216
      • getMemoryClass:16
    • Con el tamaño de almacenamiento dinámico de VM establecido en 24 MB:
      • Maxmemoria:25165824
      • getMemoryClass:16
  • Moto Droide:
    • Con el tamaño de almacenamiento dinámico de VM establecido en 24 MB:
      • Maxmemoria:25165824
      • getMemoryClass:24
    • Con el tamaño de almacenamiento dinámico de VM establecido en 16 MB:
      • Maxmemoria:16777216
      • getMemoryClass:24
  • Nexus Uno:
    • Con el tamaño de almacenamiento dinámico de VM establecido en 32 MB:
      • Maxmemoria:33554432
      • getMemoryClass:32
    • Con el tamaño de almacenamiento dinámico de VM establecido en 24 MB:
      • Maxmemoria:25165824
      • getMemoryClass:32
  • Viewsonic GTab:
    • Con el tamaño de almacenamiento dinámico de VM establecido en 32:
      • Maxmemoria:33554432
      • getMemoryClass:32
    • Con el tamaño de almacenamiento dinámico de VM establecido en 64:
      • Maxmemoria:67108864
      • getMemoryClass:32

Además de lo anterior, probé en una tableta Paladin Novo7 con Ice Cream Sandwich.Esta era esencialmente una versión estándar de ICS, excepto que he rooteado la tableta a través de 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, estos son los resultados:

  • Novo7
    • Maxmemoria:62914560
    • getMemoryClass:60

También (por Kishore en un comentario a continuación):

  • HTC One X
    • Maxmemoria:67108864
    • getMemoryClass:64

Y (según el comentario de akauppi):

  • Samsung Galaxy Core Plus
    • Maxmemoria:(No especificado en el comentario)
    • getMemoryClass:48
    • Grandememoriaclase:128

Por un comentario de cmcromance:

  • Galaxy S3 (Jelly Bean) montón grande
    • Maxmemoria:268435456
    • getMemoryClass:64

Y (según los comentarios de tencent):

  • LG Nexus 5 (4.4.3) normal
    • Maxmemoria:201326592
    • getMemoryClass:192
  • LG Nexus 5 (4.4.3) gran montón
    • Maxmemoria:536870912
    • getMemoryClass:192
  • Galaxy Nexus (4.3) normal
    • Maxmemoria:100663296
    • getMemoryClass:96
  • Galaxy Nexus (4.3) gran montón
    • Maxmemoria:268435456
    • getMemoryClass:96
  • Galaxy S4 Edición Play Store (4.4.2) normal
    • Maxmemoria:201326592
    • getMemoryClass:192
  • Galaxy S4 Play Store Edition (4.4.2) montón grande
    • Maxmemoria:536870912
    • getMemoryClass:192

Otros Dispositivos

  • Huawei Nexus 6P (6.0.1) normal
    • Maxmemoria:201326592
    • getMemoryClass:192

No he probado estos dos métodos usando la opción especial de manifiesto android:largeHeap="true" disponible desde Honeycomb, pero gracias a cmcromance y tencent tenemos algunos valores de muestra de largeHeap, como se informó anteriormente.

Mi expectativa (lo que parece ser compatible con los grandes números 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() mientras te vas getMemoryClass() sola.Hay otro método, getLargeMemoryClass (), que indica cuánta memoria se permite para una aplicación que usa la configuración largeHeap.La documentación de getLargeMemoryClass () dice: "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 adiviné correctamente, 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 la memoria adicional, probablemente no se reproducirá tan bien con cualquier otra aplicación que el usuario esté ejecutando al mismo tiempo).

Tenga en cuenta que la clase de memoria aparentemente no necesita ser un múltiplo de 8 MB.

Podemos ver en lo anterior que el getMemoryClass() el resultado no cambia para una configuración de dispositivo/sistema operativo determinada, mientras que el valor de maxMemory() cambia cuando el usuario establece 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 almacenamiento dinámico, puedo ejecutarlo sin errores, incluso cuando se permite que mi uso de memoria aumente a 20 MB (presumiblemente podría ir tan alto como 24 MB, aunque no lo he intentado).Pero otras aplicaciones de tamaño similar pueden borrarse de la memoria como resultado de la pigginess de mi propia aplicación.Y, a la inversa, mi la aplicación puede borrarse de la memoria si el usuario coloca en primer plano estas otras aplicaciones de alto mantenimiento.

Por lo tanto, no puede superar la cantidad de memoria especificada por maxMemory().Y, deberías prueba para 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 una manera que conserve la memoria.

Finalmente, si planea superar la cantidad de megabytes especificados en getMemoryClass(), mi consejo sería trabajar arduamente para guardar y restaurar el estado de su aplicación, de modo que la experiencia del usuario sea prácticamente ininterrumpida si un onStop() / onResume() se produce el ciclo.

En mi caso, por razones de rendimiento, estoy limitando mi aplicación a dispositivos con 2.2 y versiones posteriores, y eso significa que casi todos los dispositivos que ejecuten mi aplicación tendrán una memoryClass de 24 o superior.Así que 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 puede 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 anterior (por ejemplo, un G1).Cuando se encuentre con una configuración de este tipo, idealmente, debería reducir el uso de la memoria, incluso si maxMemory() te está diciendo que puedes ir mucho más alto que los 16 MB que getMemoryClass() te está diciendo que tú debería sea objetivo.Y si no puede garantizar de manera confiable que su aplicación se mantenga dentro de ese presupuesto, al menos asegúrese de que onStop() / onResume() funciona a la perfección.

getMemoryClass(), como lo indicó Diane Hackborn (hackbod) anteriormente, solo está disponible de nuevo al nivel de API 5 (Android 2.0), por lo que, como ella aconseja, puede suponer 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 las aplicaciones que ocupan un espacio de almacenamiento dinámico de no más de 16 MB.

Por contraste, 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 hacer vea que en mis versiones de CyanogenMod (mucho más tarde), 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 le sugiero que continúe probando el maxMemory() valor, incluso para versiones del SO anteriores a la 2.0.Incluso es posible que deba negarse a ejecutar en el improbable caso de que este valor se establezca incluso por debajo de 16 MB, si necesita tener más de maxMemory() indica que está permitido.

Otros consejos

API es:

  

Esta se introdujo en 2,0 donde aparecieron los dispositivos de memoria de mayor tamaño. Se puede asumir que los dispositivos que ejecutan versiones anteriores del sistema operativo están utilizando la clase de memoria original (16).

Debug.getNativeHeapSize() hará el truco, diría yo. Ha estado ahí desde 1.0, sin embargo.

La clase Debug tiene un montón de grandes métodos para el seguimiento de las asignaciones y otros problemas de rendimiento. Además, si usted necesita para detectar una situación de poca memoria, echa un vistazo a Activity.onLowMemory().

Así es como se hace:

Cómo el tamaño del montón max que la aplicación puede utilizar:

Runtime runtime = Runtime.getRuntime();
long maxMemory=runtime.maxMemory();

Obtener la cantidad de la pila utiliza actualmente su aplicación:

long usedMemory=runtime.totalMemory() - runtime.freeMemory();

Obtener la cantidad de la pila de su aplicación puede utilizar (memoria disponible):

long availableMemory=maxMemory-usedMemory;

Y, para dar formato a cada uno de ellos muy bien, puede utilizar:

String formattedMemorySize=Formatter.formatShortFileSize(context,memorySize); 

Esto devuelve el tamaño máximo de almacenamiento dinámico en bytes:

Runtime.getRuntime().maxMemory()

I estaba usando ActivityManager.getMemoryClass (), pero en CyanogenMod 7 (no he probado en otro lugar) devuelve un valor incorrecto si los conjuntos de usuarios montón tamaño manualmente.

Algunas operaciones son más rápidas que gestionar el espacio de montón de Java. operaciones Retrasar durante algún tiempo pueden liberar espacio en la memoria. Puede utilizar este método para escapar de error tamaño de la pila:

waitForGarbageCollector(new Runnable() {
  @Override
  public void run() {

    // Your operations.
  }
});

/**
 * Measure used memory and give garbage collector time to free up some
 * space.
 *
 * @param callback Callback operations to be done when memory is free.
 */
public static void waitForGarbageCollector(final Runnable callback) {

  Runtime runtime;
  long maxMemory;
  long usedMemory;
  double availableMemoryPercentage = 1.0;
  final double MIN_AVAILABLE_MEMORY_PERCENTAGE = 0.1;
  final int DELAY_TIME = 5 * 1000;

  runtime =
    Runtime.getRuntime();

  maxMemory =
    runtime.maxMemory();

  usedMemory =
    runtime.totalMemory() -
    runtime.freeMemory();

  availableMemoryPercentage =
    1 -
    (double) usedMemory /
    maxMemory;

  if (availableMemoryPercentage < MIN_AVAILABLE_MEMORY_PERCENTAGE) {

    try {
      Thread.sleep(DELAY_TIME);
    } catch (InterruptedException e) {
      e.printStackTrace();
    }

    waitForGarbageCollector(
      callback);
  } else {

    // Memory resources are availavle, go to next operation:

    callback.run();
  }
}

Asus Nexus 7 (2013) 32Gig: getMemoryClass () = 192 MaxMemory () = 201326592

Me cometió el error de creación de prototipos de mi juego en el Nexus 7, y luego descubrir que no tiene memoria casi inmediatamente en 4,04 tabletas genéricas de mi esposa (memoryclass 48, maxmemory 50331648)

Voy a tener que reestructurar mi proyecto para cargar menos recursos cuando se determino memoryclass es baja.
¿Hay alguna manera en Java para ver el tamaño de pila actual? (Puedo ver claramente en la LogCat al depurar, pero me gustaría una manera de verlo en código para adaptarse, como si currentheap> (MaxMemory / 2) mapas de bits de descarga de alta calidad de carga baja calidad

¿Quiere decir programáticamente, o simplemente mientras estás desarrollar y depurar? En este último caso, se puede ver esa información desde la perspectiva DDMS en Eclipse. Cuando el emulador (posiblemente incluso teléfono físico que está enchufado) se está ejecutando, aparecerá una lista de los procesos activos en una ventana de la izquierda. Puede seleccionarlo y hay una opción para realizar un seguimiento de las asignaciones del montón.

Runtime rt = Runtime.getRuntime();
rt.maxMemory()

valor es b

ActivityManager am = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
am.getMemoryClass()

valor es MB

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top