J'ai développé une application qui utilise beaucoup d'images sur Android.
L'application exécute une fois, remplit les informations sur l'écran ( Layouts
, Listviews
, Textviews
, ImageViews
, etc.) et l' utilisateur lit les informations.
Il n'y a pas d'animation, pas d'effets spéciaux ou quoi que ce soit qui puisse remplir la mémoire. Parfois, les tirages peuvent changer. Certaines sont des ressources Android et d'autres sont des fichiers enregistrés dans un dossier de la SDCARD.
Ensuite, l'utilisateur quitte (la onDestroy
méthode est exécutée et l'application reste en mémoire par la machine virtuelle), puis à un moment donné, l'utilisateur entre à nouveau.
Chaque fois que l'utilisateur entre dans l'application, je peux voir la mémoire augmenter de plus en plus jusqu'à ce que l'utilisateur obtienne le fichier java.lang.OutOfMemoryError
.
Alors, quelle est la meilleure / correcte façon de gérer de nombreuses images?
Dois-je les mettre dans des méthodes statiques pour qu'elles ne soient pas chargées tout le temps? Dois-je nettoyer la mise en page ou les images utilisées dans la mise en page d'une manière spéciale?
la source
Réponses:
Il semble que vous ayez une fuite de mémoire. Le problème n'est pas de gérer beaucoup d'images, c'est que vos images ne sont pas désallouées lorsque votre activité est détruite.
Il est difficile de dire pourquoi sans regarder votre code. Cependant, cet article contient quelques conseils qui pourraient vous aider:
http://android-developers.blogspot.de/2009/01/avoiding-memory-leaks.html
En particulier, l'utilisation de variables statiques risque d'aggraver les choses, pas de les améliorer. Vous devrez peut-être ajouter un code qui supprime les rappels lorsque votre application est redessinée - mais encore une fois, il n'y a pas suffisamment d'informations ici pour être sûr.
la source
L'une des erreurs les plus courantes que j'ai trouvées lors du développement d'applications Android est l'erreur «java.lang.OutOfMemoryError: Bitmap Size Exceeds VM Budget». J'ai trouvé cette erreur fréquemment sur les activités utilisant beaucoup de bitmaps après avoir changé d'orientation: l'activité est détruite, créée à nouveau et les mises en page sont «gonflées» à partir du XML consommant la mémoire VM disponible pour les bitmaps.
Les bitmaps de la disposition d'activité précédente ne sont pas correctement désalloués par le garbage collector car ils ont croisé des références à leur activité. Après de nombreuses expériences, j'ai trouvé une assez bonne solution à ce problème.
Tout d'abord, définissez l'attribut "id" sur la vue parente de votre mise en page XML:
Ensuite, sur la
onDestroy()
méthode de votre activité, appelez launbindDrawables()
méthode en passant une référence à la vue parent, puis effectuez unSystem.gc()
.Cette
unbindDrawables()
méthode explore l'arborescence des vues de manière récursive et:la source
Pour éviter ce problème, vous pouvez utiliser la méthode native
Bitmap.recycle()
avantnull
-ingBitmap
object (ou définir une autre valeur). Exemple:Et ensuite tu peux changer
myBitmap
sans appelerSystem.gc()
comme:la source
J'ai rencontré ce problème exact. Le tas est assez petit, donc ces images peuvent devenir incontrôlables assez rapidement en ce qui concerne la mémoire. Une façon consiste à donner au ramasse-miettes un indice pour collecter de la mémoire sur un bitmap en appelant sa méthode de recyclage .
En outre, la méthode onDestroy n'est pas garantie d'être appelée. Vous souhaiterez peut-être déplacer cette logique / nettoyage dans l'activité onPause. Consultez le diagramme / tableau du cycle de vie des activités sur cette page pour plus d'informations.
la source
onPause
suggestion. J'utilise 4 onglets avec desBitmap
images dans tous, donc la destruction de l'activité peut ne pas se produire trop tôt (si l'utilisateur parcourt tous les onglets).Cette explication peut vous aider: http://code.google.com/p/android/issues/detail?id=8488#c80
"Astuces rapides:
1) N'appelez JAMAIS System.gc () vous-même. Cela a été propagé comme un correctif ici, et cela ne fonctionne pas. Ne fais pas ça. Si vous avez remarqué dans mon explication, avant d'obtenir une OutOfMemoryError, la JVM exécute déjà un garbage collection donc il n'y a aucune raison d'en refaire un (cela ralentit votre programme). Faire un à la fin de votre activité ne fait que dissimuler le problème. Cela peut accélérer le placement du bitmap dans la file d'attente du finaliseur, mais il n'y a aucune raison pour laquelle vous n'auriez pas pu simplement appeler recycle sur chaque bitmap.
2) Appelez toujours recycle () sur les bitmaps dont vous n'avez plus besoin. À tout le moins, dans le onDestroy de votre activité, parcourez et recyclez tous les bitmaps que vous utilisiez. De plus, si vous voulez que les instances bitmap soient collectées plus rapidement à partir du tas dalvik, cela ne fait pas de mal d'effacer les références au bitmap.
3) L'appel de recycle () puis de System.gc () risque de ne pas supprimer le bitmap du tas Dalvik. Ne vous inquiétez pas de cela. recycle () a fait son travail et a libéré la mémoire native, il faudra juste un certain temps pour suivre les étapes que j'ai décrites plus tôt pour supprimer le bitmap du tas Dalvik. Ce n'est PAS un gros problème car la grande partie de la mémoire native est déjà libre!
4) Supposons toujours qu'il y ait un bogue dans le framework en dernier. Dalvik fait exactement ce qu'il est censé faire. Ce n'est peut-être pas ce que vous attendez ou ce que vous voulez, mais c'est ainsi que cela fonctionne. "
la source
J'ai eu exactement le même problème. Après quelques tests, j'ai constaté que cette erreur apparaît pour la mise à l'échelle de grandes images. J'ai réduit la mise à l'échelle de l'image et le problème a disparu.
PS Au début, j'ai essayé de réduire la taille de l'image sans réduire la taille de l'image. Cela n'a pas arrêté l'erreur.
la source
Les points suivants m'ont vraiment beaucoup aidé. Il peut y avoir d'autres points aussi, mais ceux-ci sont très cruciaux:
la source
Je suggère un moyen pratique de résoudre ce problème. Attribuez simplement la valeur de l'attribut "android: configChanges" comme suit dans Mainfest.xml pour votre activité erronée. comme ça:
la première solution que j'ai donnée avait vraiment réduit la fréquence des erreurs OOM à un niveau bas. Mais cela n'a pas totalement résolu le problème. Et puis je donnerai la 2ème solution:
Comme le MOO l'a détaillé, j'ai utilisé trop de mémoire d'exécution. Donc, je réduis la taille de l'image en ~ / res / drawable de mon projet. Comme une image surqualifiée qui a une résolution de 128X128, pourrait être redimensionnée à 64x64, ce qui conviendrait également à mon application. Et après l'avoir fait avec une pile d'images, l'erreur MOO ne se reproduit plus.
la source
Je suis moi aussi frustré par le bug de la mémoire. Et oui, j'ai moi aussi trouvé que cette erreur apparaît souvent lors de la mise à l'échelle des images. Au début, j'ai essayé de créer des tailles d'image pour toutes les densités, mais j'ai trouvé que cela augmentait considérablement la taille de mon application. Je n'utilise donc plus qu'une seule image pour toutes les densités et la mise à l'échelle de mes images.
Mon application lèverait une erreur de mémoire excessive chaque fois que l'utilisateur passait d'une activité à une autre. Définir mes drawables sur null et appeler System.gc () ne fonctionnait pas, ni recycler mes bitmapDrawables avec getBitMap (). Recycle (). Android continuerait à lancer l'erreur de mémoire morte avec la première approche, et il lancerait un message d'erreur de canevas chaque fois qu'il essayait d'utiliser une image bitmap recyclée avec la deuxième approche.
J'ai adopté une troisième approche. J'ai défini toutes les vues sur nulles et l'arrière-plan sur noir. Je fais ce nettoyage dans ma méthode onStop (). C'est la méthode qui est appelée dès que l'activité n'est plus visible. Si vous le faites dans la méthode onPause (), les utilisateurs verront un fond noir. Pas idéal. Quant à le faire dans la méthode onDestroy (), il n'y a aucune garantie qu'il sera appelé.
Pour éviter qu'un écran noir ne se produise si l'utilisateur appuie sur le bouton retour de l'appareil, je recharge l'activité dans la méthode onRestart () en appelant les méthodes startActivity (getIntent ()) puis finish ().
Remarque: il n'est pas vraiment nécessaire de changer l'arrière-plan en noir.
la source
Les méthodes BitmapFactory.decode *, abordées dans la leçon Charger efficacement de gros bitmaps , ne doivent pas être exécutées sur le thread principal de l'interface utilisateur si les données source sont lues à partir du disque ou d'un emplacement réseau (ou en fait de toute source autre que la mémoire). Le temps de chargement de ces données est imprévisible et dépend de divers facteurs (vitesse de lecture à partir du disque ou du réseau, taille de l'image, puissance du processeur, etc.). Si l'une de ces tâches bloque le thread d'interface utilisateur, le système marque votre application comme non réactive et l'utilisateur a la possibilité de la fermer (voir Conception pour la réactivité pour plus d'informations).
la source
Eh bien, j'ai essayé tout ce que j'ai trouvé sur Internet et aucun d'entre eux n'a fonctionné. L'appel de System.gc () ne fait que ralentir la vitesse de l'application. Le recyclage des bitmaps dans onDestroy ne fonctionnait pas non plus pour moi.
La seule chose qui fonctionne maintenant est d'avoir une liste statique de tous les bitmap afin que les bitmaps survivent après un redémarrage. Et utilisez simplement les bitmaps enregistrés au lieu d'en créer de nouveaux à chaque redémarrage de l'activité.
Dans mon cas, le code ressemble à ceci:
la source
J'ai eu le même problème avec la commutation des images d'arrière-plan avec des tailles raisonnables. J'ai obtenu de meilleurs résultats en définissant ImageView sur null avant de mettre une nouvelle image.
la source
FWIW, voici un cache bitmap léger que j'ai codé et utilisé depuis quelques mois. Ce n'est pas tout-les-cloches-et-sifflets, alors lisez le code avant de l'utiliser.
la source