web-dev-qa-db-fra.com

Détecter la taille du segment d’application dans Android

Comment détecter par programme la taille de segment d'application disponible pour une Android app?

J'ai entendu dire qu'une fonction le fait dans les versions ultérieures du SDK. Dans tous les cas, je cherche une solution qui fonctionne à partir de 1,5 jour.

142
hpique

Il existe deux manières de penser à votre phrase "taille de segment d'application disponible":

  1. Combien de tas mon application peut-elle utiliser avant qu'une erreur grave ne soit déclenchée? Et

  2. Combien de tas devrait mon utilisation de l'application, compte tenu des contraintes de la version de l'OS et de * Android) du matériel de l'appareil de l'utilisateur?

Il existe une méthode différente pour déterminer chacun des éléments ci-dessus.

Pour le point 1 ci-dessus: maxMemory()

qui peut être invoqué (par exemple, dans la méthode onCreate() de votre activité principale) comme suit:

Runtime rt = Runtime.getRuntime();
long maxMemory = rt.maxMemory();
Log.v("onCreate", "maxMemory:" + Long.toString(maxMemory));

Cette méthode vous indique combien octets de tas votre application est autorisée à utiliser.

Pour le point 2 ci-dessus: getMemoryClass()

qui peut être invoqué comme suit:

ActivityManager am = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
int memoryClass = am.getMemoryClass();
Log.v("onCreate", "memoryClass:" + Integer.toString(memoryClass));

Cette méthode vous indique approximativement combien mégaoctets de tas votre application devrait utiliser si elle veut être correctement respectueuse des limites du périphérique actuel, et du Les droits des autres applications de s'exécuter sans être forcés à plusieurs reprises dans le cycle onStop()/onResume() _ _ car ils sont vides de mémoire alors que votre application éléphantine prend un bain dans le Android jacuzzi.

Pour autant que je sache, cette distinction n’est pas clairement documentée, mais j’ai testé cette hypothèse sur cinq appareils différents Android (voir ci-dessous) et j’ai confirmé à ma propre satisfaction qu’il s’agissait d’une interprétation correcte. .

Pour une version stockée d'Android, maxMemory() renverra généralement environ le même nombre de mégaoctets que celui indiqué dans getMemoryClass() (c'est-à-dire environ un million de fois la dernière valeur).

La seule situation (dont je suis au courant) pour laquelle les deux méthodes peuvent diverger est sur un périphérique racine exécutant une version Android telle que CyanogenMod, qui permet à l'utilisateur de sélectionner manuellement quelle taille de tas devrait être autorisée pour chaque application. Dans CM, par exemple, cette option apparaît sous "Paramètres CyanogenMod"/"Performances"/"Taille de la mémoire virtuelle".

REMARQUE: Sachez que vous pouvez paramétrer manuellement cette valeur, en particulier si vous sélectionnez une valeur inférieure à la normale pour votre appareil.

Voici mes résultats de test montrant les valeurs renvoyées par maxMemory() et getMemoryClass() pour quatre périphériques différents exécutant CyanogenMod, en utilisant deux valeurs de segment différentes (définies manuellement) pour chacun:

  • G1:
    • Avec VM Taille de segment définie sur 16 Mo:
      • maxMémoire: 16777216
      • getMemoryClass: 16
    • Avec VM Taille de segment définie sur 24 Mo:
      • maxMémoire: 25165824
      • getMemoryClass: 16
  • Moto Droid:
    • Avec VM Taille de segment définie sur 24 Mo:
      • maxMémoire: 25165824
      • getMemoryClass: 24
    • Avec VM Taille de segment définie sur 16 Mo:
      • maxMémoire: 16777216
      • getMemoryClass: 24
  • Nexus One:
    • Avec VM Taille de segment de mémoire définie sur 32 Mo:
      • maxMemory: 33554432
      • getMemoryClass: 32
    • Avec VM Taille du segment de mémoire définie sur 24 Mo:
      • maxMémoire: 25165824
      • getMemoryClass: 32
  • GTab Viewsonic:
    • Avec VM Taille de segment définie sur 32:
      • maxMemory: 33554432
      • getMemoryClass: 32
    • Avec VM Taille de segment définie sur 64:
      • maxMemory: 67108864
      • getMemoryClass: 32

En plus de ce qui précède, j'ai testé sur un comprimé de Novo7 Paladin un sandwich à la crème glacée. Il s'agissait essentiellement d'une version stockée d'ICS, à l'exception du fait que la tablette a été intégrée à l'aide d'un processus simple qui ne remplace pas le système d'exploitation dans son intégralité, et ne fournit notamment aucune interface permettant de régler manuellement la taille du segment de mémoire.

Pour cet appareil, voici les résultats:

  • Novo7
    • maxMemory: 62914560
    • getMemoryClass: 60

Aussi (par Kishore dans un commentaire ci-dessous):

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

Et (par le commentaire d'Akauppi):

  • Samsung Galaxy Core Plus
    • maxMemory: (Non spécifié dans le commentaire)
    • getMemoryClass: 48
    • largeMemoryClass: 128

Par un commentaire de cmcromance:

  • Galaxie S3 (haricot gelée) gros tas
    • maxMémoire: 268435456
    • getMemoryClass: 64

Et (commentaires de tencent):

  • LG Nexus 5 (4.4.3) normal
    • maxMemory: 201326592
    • getMemoryClass: 192
  • LG Nexus 5 (4.4.3) grand tas
    • maxMemory: 536870912
    • getMemoryClass: 192
  • Galaxy Nexus (4.3) normal
    • maxMemory: 100663296
    • getMemoryClass: 96
  • Galaxy Nexus (4.3) grand tas
    • maxMémoire: 268435456
    • getMemoryClass: 96
  • Galaxy S4 Play Store Edition (4.4.2) normal
    • maxMemory: 201326592
    • getMemoryClass: 192
  • Galaxy S4 Play Store Edition (4.4.2) grand tas
    • maxMemory: 536870912
    • getMemoryClass: 192

Autres appareils

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

Je n'ai pas testé ces deux méthodes avec Android: largeHeap = "true" option manifeste disponible depuis Honeycomb, mais grâce à cmcromance et à tencent, nous avons quelques exemples de valeurs largeHeap, comme indiqué ci-dessus.

My expectation (qui semble être pris en charge par les nombres largeHeap ci-dessus) serait que cette option aurait un effet similaire à celui de la définition manuelle du segment de mémoire via un système d'exploitation enraciné - autrement dit, il augmenterait la valeur. de maxMemory() en laissant getMemoryClass() seul. Une autre méthode, getLargeMemoryClass (), indique la quantité de mémoire autorisée pour une application à l'aide du paramètre largeHeap. La documentation de getLargeMemoryClass () indique que "la plupart des applications ne devraient pas avoir besoin de cette quantité de mémoire, mais doivent rester à la limite avec la limite getMemoryClass ()".

Si j’ai bien deviné, l’utilisation de cette option présenterait les mêmes avantages (et périls) que l’utilisation de l’espace laissé disponible par un utilisateur qui a gravé le tas au moyen d’un système d’exploitation enraciné (par exemple, si votre application utilise la mémoire supplémentaire, il ne jouera probablement pas aussi bien avec toutes les autres applications que l'utilisateur exécute en même temps).

Notez que la classe de mémoire ne doit apparemment pas être un multiple de 8 Mo.

Nous pouvons voir de ce qui précède que le résultat getMemoryClass() ne change pas pour une configuration donnée de périphérique/système d'exploitation, tandis que la valeur maxMemory () change lorsque le segment de mémoire est défini différemment par l'utilisateur.

Mon expérience personnelle est que sur le G1 (qui a une classe de mémoire de 16), si je sélectionne manuellement 24 Mo comme taille de segment de mémoire, je peux fonctionner sans erreur, même si mon utilisation de la mémoire est autorisée à atteindre 20 Mo (sans doute aller aussi haut que 24MB, bien que je n'ai pas essayé ceci). Mais d'autres applications similaires de grande taille peuvent être vides de mémoire en raison de la pigmentation de ma propre application. Et, à l’inverse, l’application my peut être effacée de la mémoire si ces autres applications nécessitant une maintenance élevée sont mises au premier plan par l’utilisateur.

Donc, vous ne pouvez pas dépasser la quantité de mémoire spécifiée par maxMemory(). Et vous devriez essayer rester dans les limites spécifiées par getMemoryClass(). Une façon de le faire, si tout le reste échoue, pourrait être de limiter les fonctionnalités de tels périphériques de manière à conserver la mémoire.

Enfin, si vous envisagez de dépasser le nombre de mégaoctets spécifié dans getMemoryClass(), je vous conseillerais de travailler longuement et dur à la sauvegarde et à la restauration de l'état de votre application, afin que l'expérience de l'utilisateur soit pratiquement ininterrompue. si un cycle onStop()/onResume() se produit.

Dans mon cas, pour des raisons de performances, je limite mon application aux appareils fonctionnant sous 2.2 et versions ultérieures, ce qui signifie que presque tous les appareils exécutant mon application auront une memoryClass de 24 ou plus. Ainsi, je peux concevoir jusqu'à occuper jusqu'à 20 Mo de disque et être assez confiant que mon application jouera bien avec les autres applications que l'utilisateur peut exécuter en même temps.

Mais il y aura toujours quelques utilisateurs enracinés qui ont chargé une version 2.2 ou supérieure de Android sur un appareil plus ancien (par exemple, un G1). Lorsque vous rencontrez une telle configuration, idéalement, vous devriez pour réduire votre utilisation de la mémoire, même si maxMemory() vous dit que vous pouvez aller beaucoup plus haut que les 16 Mo que getMemoryClass() vous dit que vous devriez = être ciblé. Et si vous ne pouvez pas vous assurer de manière fiable que votre application vivra dans les limites de ce budget, assurez-vous au moins que onStop()/onResume() fonctionne de manière transparente.

getMemoryClass(), comme indiqué par Diane Hackborn (hackbod) ci-dessus, n'est disponible que depuis le niveau 5 de l'API (Android 2.0). Par conséquent, comme elle le conseille, vous pouvez supposer que le matériel physique de tout périphérique exécutant une La version précédente du système d'exploitation est conçue pour prendre en charge de manière optimale les applications occupant un espace de travail de 16 Mo maximum.

En revanche, maxMemory(), selon la documentation, est disponible jusqu'au niveau 1 de l'API. maxMemory(), dans une version antérieure à la version 2.0, renverra probablement une valeur de 16 Mo, mais Je do Je constate que, dans mes versions (beaucoup plus tard) de CyanogenMod, l'utilisateur peut sélectionner une valeur de segment aussi faible que 12 Mo, ce qui entraînerait vraisemblablement une limite de segment inférieure, et je suggère donc que vous continuez à tester la valeur maxMemory(), même pour les versions du système d'exploitation antérieures à 2.0. Vous devrez peut-être même refuser l'exécution dans le cas improbable où cette valeur est définie même si elle est inférieure à 16 Mo, si vous avez besoin de plus de maxMemory() indique que l'autorisation est autorisée.

441
Carl

L'officiel API est:

Ceci a été introduit dans la version 2.0 où de plus grands périphériques de mémoire sont apparus. Vous pouvez supposer que les périphériques exécutant des versions antérieures du système d'exploitation utilisent la classe de mémoire d'origine (16).

20
hackbod

Debug.getNativeHeapSize() fera l'affaire, je devrais penser. Il est là depuis 1.0, cependant.

La classe Debug propose de nombreuses méthodes de suivi des allocations et autres problèmes de performances. De plus, si vous devez détecter une situation de mémoire insuffisante, consultez Activity.onLowMemory().

13
Neil Traft

Voici comment vous le faites:

Obtenir la taille maximale du tas que l'application peut utiliser:

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

Obtention de la quantité de mémoire utilisée actuellement par votre application:

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

Obtenir combien de mémoire votre application peut maintenant utiliser (mémoire disponible):

long availableMemory=maxMemory-usedMemory;

Et, pour bien les formater, vous pouvez utiliser:

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

Cela retourne la taille maximale du tas en octets:

Runtime.getRuntime().maxMemory()

J'utilisais ActivityManager.getMemoryClass () mais sur CyanogenMod 7 (je ne l'ai pas testé ailleurs), il renvoie une valeur incorrecte si l'utilisateur définit manuellement la taille de segment de mémoire.

4
fhucho

Certaines opérations sont plus rapides que Java heap space manager. Retarder les opérations pendant un certain temps peut libérer de l'espace mémoire. Vous pouvez utiliser cette méthode pour échapper à une erreur liée à la taille d'un tas:

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();
  }
}
2
Zon

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

J'ai fait l'erreur de prototyper mon jeu sur le Nexus 7, puis de découvrir qu'il manquait presque immédiatement de mémoire sur la tablette générique 4.04 de ma femme (memoryclass 48, maxmemory 50331648).

Je devrai restructurer mon projet pour charger moins de ressources lorsque je déterminerai que memoryclass est faible.
Existe-t-il un moyen dans Java de voir la taille actuelle du tas? (Je le vois clairement dans le logCat lors du débogage, mais j'aimerais un moyen de le voir dans code à adapter, comme si currentheap> (maxmemory/2) décharge des bitmaps de haute qualité

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

la valeur est b

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

la valeur est MB

0
qinqie

Voulez-vous dire par programmation ou juste pendant que vous développez et déboguez? Dans ce dernier cas, vous pouvez voir ces informations de la perspective DDMS dans Eclipse. Lorsque votre émulateur (voire même un téléphone physique branché) est en cours d'exécution, il répertorie les processus actifs dans une fenêtre à gauche. Vous pouvez le sélectionner et une option permet de suivre les allocations de tas.

0
Steve Haley