web-dev-qa-db-fra.com

Quels outils et méthodes Android fonctionnent le mieux pour détecter les fuites de mémoire / de ressources?

J'ai une Android développée) et je suis sur le point de développer une application pour téléphone où tout semble bien fonctionner et où vous souhaitez déclarer victoire et livraison, mais vous savez qu'il existe Il ne reste plus que quelques fuites de mémoire et de ressources; et il n’ya que 16 Mo de tas sur le Android et il est apparemment étonnamment facile de fuir dans un Android app .

J'ai jeté un coup d'œil autour de moi et, jusqu'à présent, je n'ai pu que creuser des informations sur "hprof" et "traceview", sans obtenir de nombreuses critiques favorables.

Quels outils ou méthodes avez-vous rencontrés ou développés et souhaitez-vous partager dans un projet de système d'exploitation?

151
jottos

L'une des erreurs les plus courantes que j'ai constatées lors du développement Android Apps est l'erreur "Java.lang.OutOfMemoryError: la taille du bitmap dépasse la valeur VM Budget"). J'ai trouvé ceci. 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 qui utilise la mémoire VM disponible pour les bitmaps.

Les bitmaps de la structure d'activité précédente ne sont pas correctement désalloués par le ramasse-miettes car ils ont croisé les références à leur activité. Après de nombreuses expériences, j'ai trouvé une très bonne solution à ce problème.

Tout d’abord, définissez l’attribut "id" sur la vue parent de votre présentation XML:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:Android="http://schemas.Android.com/apk/res/Android"
     Android:layout_width="fill_parent"
     Android:layout_height="fill_parent"
     Android:id="@+id/RootView"
     >
     ...

Ensuite, dans la méthode onDestroy () de votre activité, appelez la méthode unbindDrawables () en transmettant une référence à la vue parent, puis effectuez System.gc ().

@Override
protected void onDestroy() {
    super.onDestroy();

    unbindDrawables(findViewById(R.id.RootView));
    System.gc();
}


private void unbindDrawables(View view) {

    if (view.getBackground() != null) {
        view.getBackground().setCallback(null);
    }

    if (view instanceof ViewGroup) {
        for (int i = 0; i < ((ViewGroup) view).getChildCount(); i++) {
            unbindDrawables(((ViewGroup) view).getChildAt(i));
        }

        ((ViewGroup) view).removeAllViews();
    }
}

Cette méthode unbindDrawables () explore l'arborescence de la vue de manière récursive et:

  1. Supprime les rappels sur tous les éléments dessinables en arrière-plan
  2. Supprime les enfants sur chaque groupe de vues
90
hp.android
29
kohlerm

Principalement pour les voyageurs Google de l'avenir:

La plupart des Java ne conviennent malheureusement pas à cette tâche, car ils analysent uniquement le tas JVM. Chaque Android Application possède également un segment de mémoire natif, qui doit respecter la limite de ~ 16 Mo. Il est généralement utilisé pour les données bitmap, par exemple, ce qui vous permet de facilement échapper aux erreurs Out Of Memory même si votre machine virtuelle JVM-Heap tourne autour de 3 Mo si vous utilisez beaucoup de fonctions dessinables. .

28
Timo Ohr

La réponse de @ hp.Android fonctionne bien si vous travaillez uniquement avec des arrière-plans bitmap mais, dans mon cas, j’avais un BaseAdapter fournissant un ensemble de ImageViews pour un GridView . J'ai modifié la méthode unbindDrawables() comme indiqué afin que la condition soit:

if (view instanceof ViewGroup && !(view instanceof AdapterView)) {
  ...
}

mais le problème est alors que la méthode récursive ne traite jamais les enfants du AdapterView. Pour remédier à cela, j'ai plutôt fait ce qui suit:

if (view instanceof ViewGroup) {
  ViewGroup viewGroup = (ViewGroup) view;
  for (int i = 0; i < viewGroup.getChildCount(); i++)
    unbindDrawables(viewGroup.getChildAt(i));

  if (!(view instanceof AdapterView))
    viewGroup.removeAllViews();
}

afin que les enfants de AdapterView soient toujours traités - la méthode ne tente tout simplement pas de supprimer tous les enfants (ce qui n'est pas pris en charge).

Cela ne résout toutefois pas le problème puisque ImageViews gère un bitmap qui n’est pas leur arrière-plan. J'ai donc ajouté ce qui suit. Ce n'est pas idéal mais ça marche:

if (view instanceof ImageView) {
  ImageView imageView = (ImageView) view;
  imageView.setImageBitmap(null);
}

Globalement, la méthode unbindDrawables() est alors:

private void unbindDrawables(View view) {
  if (view.getBackground() != null)
    view.getBackground().setCallback(null);

  if (view instanceof ImageView) {
    ImageView imageView = (ImageView) view;
    imageView.setImageBitmap(null);
  } else if (view instanceof ViewGroup) {
    ViewGroup viewGroup = (ViewGroup) view;
    for (int i = 0; i < viewGroup.getChildCount(); i++)
    unbindDrawables(viewGroup.getChildAt(i));

    if (!(view instanceof AdapterView))
      viewGroup.removeAllViews();
  }
}

J'espère qu'il existe une approche plus fondée sur des principes pour libérer de telles ressources.

20
darrenp

Bonne conversation Google I/O (2011) sur la gestion de la mémoire dans Android, ainsi que des détails sur les outils et les techniques de profilage de la mémoire:
http://www.youtube.com/watch?v=_CruQY55HOk

12
greg7gkb

Valgrind a été porté sur Android (sponsorisé par Mozilla). Voir Valgrind sur Android - État actuel) et Support Exécuter Valgrind pour Android sur ARM (commentaire 67).

4
jww

Eh bien, ce sont les outils qui se rattachent aux formats uniques que Android utilise ...

Avez-vous essayé de simuler des zones de code à l’aide du framework Android Mock?

1
Fred Grott