48 votes

Handlers et fuites de mémoire dans Android

Veuillez consulter le code ci-dessous :

public class MyGridFragment extends Fragment{

    Handler myhandler = new Handler() {
        @Override
        public void handleMessage(Message message) {
            switch (message.what) {
                case 2:   
                    ArrayList<HashMap<String,String>> theurls = (ArrayList<HashMap<String,String>>) message.obj;
                    urls.addAll(theurls);
                    theimageAdapter.notifyDataSetChanged();
                    dismissBusyDialog();
                    break;
            }
        }
    }
}

Lorsque j'utilise un gestionnaire comme celui-ci, j'obtiens un avertissement "le gestionnaire doit être statique, sinon il est sujet à des fuites de mémoire". Quelqu'un peut-il me dire quelle est la meilleure façon de procéder ?

2 votes

Je ne suis pas convaincu que tu utilises le manipulateur correctement. Jetez un coup d'oeil à ce guide : vogella.com/articles/AndroidPerformance/article.html . Il n'est pas déclaré comme statique dans l'exemple de code ici. :/

0 votes

Même en l'utilisant comme ça, je reçois la même erreur. Cela n'arrivait jamais avant que je mette à jour mon SDK Android la nuit dernière. Le simple fait de déclarer un gestionnaire comme une variable de classe fait apparaître cet avertissement de lint maintenant.

0 votes

Et si vous déclariez simplement votre gestionnaire statique ?

101voto

Uncle Code Monkey Points 1276

J'ai récemment mis à jour quelque chose de similaire dans mon propre code. J'ai simplement fait de la classe anonyme Handler une classe interne protégée et l'avertissement de Lint a disparu. Voyez si quelque chose comme le code ci-dessous fonctionnera pour vous :

public class MyGridFragment extends Fragment{

    static class MyInnerHandler extends Handler{
        WeakReference<MyGridFragment> mFrag;

        MyInnerHandler(MyGridFragment aFragment) {
            mFrag = new WeakReference<MyGridFragment>(aFragment);
        }

        @Override
        public void handleMessage(Message message) {
            MyGridFragment theFrag = mFrag.get();
            switch (message.what) {
            case 2:
                ArrayList<HashMap<String,String>> theurls = (ArrayList<HashMap<String,String>>) message.obj;
                theFrag.urls.addAll(theurls);
                theFrag.theimageAdapter.notifyDataSetChanged();
                theFrag.dismissBusyDialog();
                break;
            }//end switch
        }
    }
    MyInnerHandler myHandler = new MyInnerHandler(this);
}

Vous devrez peut-être changer l'endroit où j'ai mis "theFrag." car je ne pouvais que deviner ce à quoi ils faisaient référence.

0 votes

Cela semble être exactement ce que je veux. Je ne l'ai pas encore testé, mais il semble qu'il n'y ait aucune raison pour que cela ne fonctionne pas. Merci !

13 votes

Ne devrait-il pas y avoir un contrôle après mFrag.get() pour vérifier si theFrag != null parce qu'il pourrait être récupéré par les ordures.

1 votes

Cela dépend de votre cas d'utilisation. Mon code personnel rendait les définitions de classes statiques internes privées dans la plupart des cas, car la définition de classe anonyme n'était pas censée être utilisée ailleurs (même par les descendants). Lorsqu'elle est limitée à la classe dans laquelle elle est définie, chaque fois que le fragment parent est détruit, la référence à notre classe interne devrait être détruite avant que le fragment ne devienne NULL. Nous utilisons WeakReference pour que notre classe interne n'interfère pas avec la destruction de Fragment. Si vous rendez votre classe interne publique, ou si vous n'êtes pas sûr, veuillez vérifier que theFrag!=null avant de l'utiliser.

11voto

Timmmm Points 9909

Voici un petit cours assez utile que j'ai fait et que vous pouvez utiliser. Malheureusement, elle est encore assez verbeuse car vous ne pouvez pas avoir de classes internes statiques anonymes.

import java.lang.ref.WeakReference;
import android.os.Handler;
import android.os.Message;

/** A handler which keeps a weak reference to a fragment. According to
 * Android's lint, references to Handlers can be kept around for a long
 * time - longer than Fragments for example. So we should use handlers
 * that don't have strong references to the things they are handling for.
 * 
 * You can use this class to more or less forget about that requirement.
 * Unfortunately you can have anonymous static inner classes, so it is a
 * little more verbose.
 * 
 * Example use:
 * 
 *  private static class MsgHandler extends WeakReferenceHandler<MyFragment>
 *  {
 *      public MsgHandler(MyFragment fragment) { super(fragment); }
 * 
 *      @Override
 *      public void handleMessage(MyFragment fragment, Message msg)
 *      {
 *          fragment.doStuff(msg.arg1);
 *      }
 *  }
 * 
 *  // ...
 *  MsgHandler handler = new MsgHandler(this);
 */
public abstract class WeakReferenceHandler<T> extends Handler
{
    private WeakReference<T> mReference;

    public WeakReferenceHandler(T reference)
    {
        mReference = new WeakReference<T>(reference);
    }

    @Override
    public void handleMessage(Message msg)
    {
        if (mReference.get() == null)
            return;
        handleMessage(mReference.get(), msg);
    }

    protected abstract void handleMessage(T reference, Message msg);
}

0 votes

Si je n'ai pas besoin de surcharger handleMessage et que je me contente d'utiliser un gestionnaire pour afficher le Runnable, puis-je simplement utiliser new Handler()

4 votes

If (mReference.get() == null) return ; handleMessage(mReference.get(), msg) ; est une mauvaise approche parce qu'entre la vérification de nullité et handleMessage la référence peut toujours être gc'ed causant une exception de pointeur nul, donc c'est une meilleure approche de stocker la valeur retournée d'abord dans T reference = mReference.get() ; puis vérifier pour null et passer la variable locale à la méthode handleMessage.

5voto

Geobits Points 12623

Par le Modifications de l'ADT 20 il semble que vous devriez le rendre statique.

Nouveaux contrôles de peluches :

Vérifiez que les classes Fragment sont instanciables. Si vous faites accidentellement un fragment non statique, ou oubliez d'avoir un constructeur par défaut, vous pouvez rencontrer des erreurs d'exécution lorsque le système tente de réinstancier votre fragment après la configuration. erreurs d'exécution lorsque le système tente de réinstancier votre fragment après un changement de configuration.

Recherchez les fuites du manipulateur : Cette vérification permet de s'assurer qu'une classe interne de gestionnaire ne contient pas une référence implicite à sa classe externe.

0 votes

Alors, comment pensez-vous que je devrais gérer ce qui précède Avez-vous besoin de voir plus de code Si oui, faites-le moi savoir

0 votes

Si vous ne pouvez pas rendre le gestionnaire statique, la meilleure chose à faire est de le déplacer vers l'activité parente et de passer une référence au fragment.

0 votes

Merci pour vos réponses Geobits... mais la déplacer vers l'activité parentale ne fera toujours pas disparaître l'avertissement. Bien que dans mon cas, la classe intérieure ne vivra pas plus longtemps que la classe extérieure, mais se débarrasser de l'avertissement était quelque chose qui me préoccupait davantage.

4voto

QuickNick Points 1141

Si vous lisez les docs sur AccountManager ou PendingIntent, vous verrez que certaines méthodes prennent Handler comme un des arguments.

Par exemple :

  • onFinished - L'objet à rappeler lorsque l'envoi est terminé, ou null pour aucun callback.
  • handler - Handler identifiant le thread sur lequel le callback doit se produire . S'il est nul, le rappel se fera à partir du pool de threads du processus.

Imaginez la situation. Une activité appelle PendingIntent.send(...) et met la sous-classe interne non statique de Handler. Et puis l'activité est détruite. Mais la classe interne vit.

La classe interne a toujours un lien avec l'activité détruite, elle ne peut pas être collectée à la poubelle.

Si vous ne prévoyez pas d'envoyer votre gestionnaire à de telles méthodes, vous n'avez pas à vous inquiéter.

2 votes

Merci QuickNick ... Je suis d'accord avec vous lorsque vous dites "Si vous n'avez pas l'intention d'envoyer votre gestionnaire à de telles méthodes, vous n'avez pas à vous inquiéter", mais je dois me débarrasser de cet avertissement.

0voto

blurkidi Points 9

Je rencontre le même problème et je trouve que c'est l'un de ces sujets avec beaucoup de questions et peu de réponses. Ma solution est simple et j'espère qu'elle pourra aider quelqu'un :

/* BEFORE */
private Handler mHandler= new Handler() {
        @Override public void handleMessage(Message msg) {
        this.doSomething();
    };
};

Nous pouvons créer une sous-classe de Handler statique qui exécute simplement un Runnable. L'instance réelle du gestionnaire saura quoi faire par le biais du Runnable qui aura accès aux variables d'instance.

/* AFTER */
static class RunnableHandler extends Handler {
    private Runnable mRunnable;
    public RunnableHandler(Runnable runnable) { 
        mRunnable = runnable;
    }
    @Override public void handleMessage(Message msg) {
        mRunnable.run();
    };
}
private RunnableHandler mHandler = new RunnableHandler(new Runnable() {
    @Override public void run() {
        this.doSomething();
    } });

L'avertissement a disparu alors que la fonctionnalité est la même.

1 votes

Merci d'avoir répondu à Urkidi, mais pour une raison quelconque, je trouve que c'est plutôt un hack. Je ne suis pas un expert et je peux me tromper.

0 votes

J'aime bien cette façon de faire, mais j'aimerais que quelqu'un qui a plus de connaissances me dise s'il s'agit d'une simple astuce.

0 votes

Cela masque l'avertissement Lint mais ne résout pas le problème sous-jacent. Le runnable aura une référence à 'this', le et puisque le RunnableHandler garde une référence au runnable vous avez la même erreur juste avec une chaîne plus alambiquée.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X