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

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

J’ai entendu dire qu’il existe une fonction qui le fait dans les versions ultérieures du SDK. En tout cas, je cherche une solution qui fonctionne pour 1,5 et plus.

Il y a deux façons de réfléchir à la phrase “taille de tas 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 mon application doit -elle utiliser, compte tenu des contraintes de la version du système d’exploitation Android et 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 l’article 1 ci-dessus: maxMemory()

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

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

Cette méthode vous indique le nombre total d’ octets de tas que 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.toSsortingng(memoryClass)); 

Cette méthode vous indique approximativement combien de mégaoctets de tas votre application doit utiliser si elle veut respecter correctement les limites du périphérique actuel et les droits des autres applications à exécuter sans être forcée de manière répétée dans le onStop() / onResume() cycle comme ils sont rudement vidés de la mémoire pendant que votre application éléphantine prend un bain dans le jacuzzi Android.

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

Pour une version stock d’Android, maxMemory() renverra généralement environ le même nombre de mégaoctets que celui indiqué dans getMemoryClass() , soit environ un million de fois cette dernière valeur.

La seule situation (dont je suis au courant) pour laquelle les deux méthodes peuvent diverger se trouve sur un périphérique root exécutant une version Android telle que CyanogenMod, qui permet à l’utilisateur de sélectionner manuellement la taille de segment de mémoire autorisée pour chaque application. Dans CM, par exemple, cette option apparaît sous “Paramètres CyanogenMod” / “Performance” / “Taille du segment de mémoire VM”.

REMARQUE: Sachez que le réglage manuel de cette valeur peut perturber votre système, 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 tas différentes (définies manuellement) pour chacun:

  • G1:
    • Avec VM Heap Size défini sur 16 Mo:
      • maxMemory: 16777216
      • getMemoryClass: 16
    • Avec VM Heap Size défini sur 24 Mo:
      • maxMemory: 25165824
      • getMemoryClass: 16
  • Moto Droid:
    • Avec VM Heap Size défini sur 24 Mo:
      • maxMemory: 25165824
      • getMemoryClass: 24
    • Avec VM Heap Size défini sur 16 Mo:
      • maxMemory: 16777216
      • getMemoryClass: 24
  • Nexus One:
    • Avec la taille de tas VM définie sur 32 Mo:
      • maxMemory: 33554432
      • getMemoryClass: 32
    • Avec la taille de segment de mémoire virtuelle définie sur 24 Mo:
      • maxMemory: 25165824
      • GetMemoryClass: 32
  • Viewsonic GTab:
    • Avec VM Heap Size défini sur 32:
      • maxMemory: 33554432
      • getMemoryClass: 32
    • Avec VM Heap Size défini sur 64:
      • maxMemory: 67108864
      • getMemoryClass: 32

En plus de ce qui précède, j’ai testé sur une tablette Novo7 Paladin exécutant Ice Cream Sandwich. Il s’agissait essentiellement d’une version standard d’ICS, sauf que j’ai ancré la tablette dans un processus simple qui ne remplace pas l’ensemble du système d’exploitation, et en particulier ne fournit pas d’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 (d’après le commentaire d’Akauppi):

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

Par un commentaire de cmcromance:

  • Gros tas de galaxie S3 (Jelly Bean)
    • maxMemory: 268435456
    • getMemoryClass: 64

Et (commentaires de tencent):

  • LG Nexus 5 (4.4.3) normal
    • maxMemory: 201326592
    • getMemoryClass: 192
  • Grand tas de LG Nexus 5 (4.4.3)
    • maxMemory: 536870912
    • getMemoryClass: 192
  • Galaxy Nexus (4.3) normal
    • maxMemory: 100663296
    • getMemoryClass: 96
  • Galaxy Nexus (4.3) gros tas
    • maxMemory: 268435456
    • getMemoryClass: 96
  • Galaxy S4 Play Store Edition (4.4.2) normal
    • maxMemory: 201326592
    • getMemoryClass: 192
  • Galaxy S4 Play Store Edition (4.4.2) gros 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 en utilisant l’option manifeste Android: largeHeap = “true” disponible depuis Honeycomb, mais grâce à cmcromance et tencent, nous avons quelques exemples de valeurs largeHeap, comme indiqué ci-dessus.

Mon attente (qui semble être supscope par les nombres de bigHeap ci-dessus) serait que cette option aurait un effet similaire à définir manuellement le tas via un système d’exploitation rooté – c.-à-d. maxMemory() la valeur de maxMemory() tout en laissant getMemoryClass() seul. Il existe une autre méthode, getLargeMemoryClass (), qui indique la quantité de mémoire autorisée pour une application utilisant le paramètre largeHeap. La documentation de getLargeMemoryClass () indique que “la plupart des applications ne doivent pas avoir besoin de cette quantité de mémoire et doivent restr avec la limite getMemoryClass ()”.

Si j’ai bien deviné, utiliser cette option présenterait les mêmes avantages (et périls) que l’utilisation de l’espace mis à disposition par un utilisateur ayant augmenté le tas via un système d’exploitation rooté (si votre application utilise la mémoire supplémentaire, cela ne fonctionnera probablement pas aussi bien avec les autres applications que l’utilisateur exécute en même temps).

Notez que la classe de mémoire n’a apparemment pas besoin d’être un multiple de 8 Mo.

Nous pouvons voir à partir de ce qui précède que le résultat getMemoryClass() est inchangé pour une configuration de périphérique / système d’exploitation donnée, tandis que la valeur maxMemory () change lorsque le tas 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 tas, je peux exécuter sans erreur même si mon utilisation de la mémoire est autorisée à atteindre 20 Mo (probablement aller aussi haut que 24 Mo, même si je n’ai pas essayé cela). Mais d’autres applications similaires peuvent être vidées de leur mémoire suite au racisme de ma propre application. Et, à l’inverse, mon application peut être vidée de sa mémoire si ces autres applications à haute maintenance sont mises au premier plan par l’utilisateur.

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

Enfin, si vous prévoyez de dépasser le nombre de mégaoctets spécifié dans getMemoryClass() , je vous conseille de travailler longtemps et de manière intensive sur l’enregistrement et la restauration de l’état de l’application, de sorte que onStop() / onResume() cycle se produit.

Dans mon cas, pour des raisons de performances, je limite mon application aux périphériques exécutant les versions 2.2 et supérieures, ce qui signifie que presque tous les périphériques exécutant mon application auront une classe de mémoire de 24 ou plus. Donc, je peux concevoir pour occuper jusqu’à 20 Mo de tas et je suis certain que mon application jouera bien avec les autres applications que l’utilisateur peut exécuter simultanément.

Mais il y aura toujours quelques utilisateurs rootés qui ont chargé une version 2.2 ou supérieure d’Android sur un ancien appareil (par exemple, un G1). Lorsque vous rencontrez une telle configuration, idéalement, vous devriez réduire votre utilisation de mémoire, même si maxMemory() vous dit que vous pouvez aller beaucoup plus haut que les 16 Mo que getMemoryClass() vous getMemoryClass() que vous devriez cibler. Et si vous ne pouvez pas vous assurer de manière fiable que votre application fonctionnera 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 pour l’API niveau 5 (Android 2.0), et, comme elle le conseille, vous pouvez supposer que le matériel physique de tout périphérique exécutant une version antérieure du OS est conçu pour prendre en charge de manière optimale les applications occupant un espace de mémoire ne dépassant pas 16 Mo.

En revanche, maxMemory() , selon la documentation, est disponible jusqu’au niveau 1 de l’API. maxMemory() , sur une version antérieure à la version 2.0, renverra probablement une valeur de 16 Mo, mais je le vois dans versions ultérieures de CyanogenMod, l’utilisateur peut sélectionner une valeur de maxMemory() aussi faible que 12 Mo, ce qui entraînerait probablement une limite de maxMemory() inférieure, et je suggère donc de continuer à tester la valeur maxMemory() , même pour les versions du système d’exploitation antérieures 2.0. Vous pourriez même devoir refuser de courir dans le cas improbable où cette valeur serait inférieure à 16 Mo, si vous avez besoin d’avoir plus que maxMemory() indique qu’il est autorisé.

L’ API officielle est:

Cela a été introduit dans la version 2.0 où des périphériques de mémoire plus importants 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).

Je pense que Debug.getNativeHeapSize() fera l’affaire. Il a été là depuis 1.0, cependant.

La classe Debug possède de nombreuses méthodes pour suivre les allocations et autres problèmes de performance. En outre, si vous devez détecter une situation de mémoire insuffisante, consultez Activity.onLowMemory() .

Voici comment vous le faites:

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

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

Obtenir la quantité de tas que votre application utilise actuellement:

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

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

 long availableMemory=maxMemory-usedMemory; 

Et, pour bien les formater, vous pouvez utiliser:

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

Cela renvoie 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 du tas.

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

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

Je vais avoir besoin de restructurer mon projet pour charger moins de ressources lorsque je détermine que la classe de mémoire est faible.
Existe-t-il un moyen en Java de voir la taille de segment de mémoire actuelle? (Je peux le voir clairement dans le logCat lors du débogage, mais j’aimerais un moyen de le voir dans le code pour l’adapter, comme si currentheap> (maxmemory / 2) décharger des bitmaps de haute qualité charger une qualité faible

Certaines opérations sont plus rapides que le gestionnaire d’espace java. Retarder les opérations pendant un certain temps peut libérer de l’espace mémoire. Vous pouvez utiliser cette méthode pour échapper à l’erreur de taille de segment de mémoire:

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

Voulez-vous dire par programmation, ou juste pendant que vous développez et déboguez? Si c’est le cas, vous pouvez voir ces informations à partir de la perspective DDMS dans Eclipse. Lorsque votre émulateur (peut-être 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 vous avez la possibilité de suivre les allocations de tas.

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

la valeur est b

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

la valeur est MB