web-dev-qa-db-fra.com

Gestionnaires et fuites de mémoire sous Android

Veuillez regarder 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, un avertissement s'affiche: «Le gestionnaire doit être statique, sinon il est sujet aux fuites de mémoire. Quelqu'un peut-il me dire quel est le meilleur moyen de le faire?

44
Rasmus

J'ai récemment mis à jour quelque chose de similaire dans mon propre code. Je viens de faire de la classe anonyme Handler une classe interne protégée et l'avertissement 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 où je mets "theFrag". comme je pouvais seulement deviner de quoi se référait.

98
Uncle Code Monkey

Voici un petit cours quelque peu utile que j'ai créé et que vous pouvez utiliser. Malheureusement, cela reste assez verbeux 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);
}
11
Timmmm

Selon le ADT 20 Changes , il semblerait que vous deviez le rendre statique.

Nouveaux contrôles de charpie:

Vérifiez que les classes de fragments sont instanciables. Si vous faites accidentellement un fragmentez innerclass non-statique, ou oubliez un constructeur par défaut, vous pouvez lancer runtime erreurs lorsque le système tente de réinitialiser votre fragment après un changement de configuration.

Rechercher les fuites du gestionnaire: Cette vérification permet de s’assurer que la classe interne du gestionnaire ne contient pas un référence implicite à sa classe externe.

5
Geobits

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

Par exemple :

  • onFinished - L'objet sur lequel le rappel doit être effectué une fois l'envoi terminé ou la valeur null pour aucun rappel.
  • handler - Handler identifiant le thread sur lequel le rappel doit avoir lieu. Si la valeur est null, le rappel aura lieu à partir du pool de threads du processus.

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

La classe interne contient toujours un lien vers une activité détruite, elle ne peut pas être collectée.

Si vous n'envisagez pas d'envoyer votre gestionnaire à de telles méthodes, vous n'avez rien à craindre.

3
QuickNick

Je rencontre le même problème et je trouve que c’est l’un des sujets avec beaucoup de questions et peu de réponses. Ma solution est simple et j'espère que cela 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 statique Handler qui exécute simplement un Runnable. L'instance de gestionnaire réelle saura quoi faire par le biais du fichier exécutable 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.

0
blurkidi

Une solution simple pour ce cas pourrait être:

Handler handler=new Handler(new Handler.Callback() {
    @Override
    public boolean handleMessage(Message message) {
        //do your stuff here
        return false;
    } });
0
StartCoding