Comment supprimer tous les rappels d'un gestionnaire?

222

J'ai un gestionnaire de ma sous-activité qui a été appelé par l' activité principale . Ce gestionnaire est utilisé par les sous-classes de postDelaycertains Runnables, et je ne peux pas les gérer. Maintenant, dans l' onStopévénement, je dois les supprimer avant de terminer l'activité (d'une manière ou d'une autre j'ai appelé finish(), mais il appelle encore et encore). Est-il possible de supprimer tous les rappels d'un gestionnaire?

Luke Vo
la source

Réponses:

522

D'après mon expérience, cela a très bien fonctionné!

handler.removeCallbacksAndMessages(null);

Dans la documentation de removeCallbacksAndMessages, il est dit ...

Supprimez tous les messages en attente de rappels et les messages envoyés dont l'obj est un jeton. Si le jeton est null, tous les rappels et messages seront supprimés.

josh527
la source
2
@Malachiasz Je pense que je l'utiliserais dans onStop ou onPause, pour m'assurer qu'aucun message n'est traité une fois que l'activité a perdu le focus. Mais dépend de ce qui doit être fait lorsque le rappel / message est déclenché
Boy
1
Je crois avoir déjà vu NPE sur certains téléphones en faisant cela, mais cela fait un moment.
Matt Wolfe du
3
J'ai eu quelques problèmes avec removeCallbacksAndMessages(null)ne supprimerait pas certains de mes rappels. Lorsque je voulais arrêter de recevoir handler.removeCallbacksAndMessages(null)des rappels, j'appelais et définissais mon gestionnaire sur null, mais comme je recevais toujours le rappel, je rencontrais un NPE lorsque je voulais faire une boucle avec handler.postDelayed().
Snaker
@Snaker Avez-vous déjà résolu votre problème? J'ai le même problème où Handler.Callback est appelé même après avoir supprimé les rappels et les messages en définissant null.
ShrimpCrackers
1
@ShrimpCrackers J'ai découvert que garder une instance de votre exécutable et l'utiliser yourHandler.removeCallbacks(yourRunnable)était le plus fiable. J'utilise toujours ça aujourd'hui.
Snaker le
19

Pour toute Runnableinstance spécifique , appelez Handler.removeCallbacks(). Notez qu'il utilise l' Runnableinstance elle-même pour déterminer les rappels à annuler, donc si vous créez une nouvelle instance à chaque fois qu'une publication est effectuée, vous devez vous assurer que vous avez des références à l'exact Runnableà annuler. Exemple:

Handler myHandler = new Handler();
Runnable myRunnable = new Runnable() {
    public void run() {
        //Some interesting task
    }
};

Vous pouvez appeler myHandler.postDelayed(myRunnable, x)pour publier un autre rappel dans la file d'attente de messages à d'autres endroits de votre code et supprimer tous les rappels en attente avecmyHandler.removeCallbacks(myRunnable)

Malheureusement, vous ne pouvez pas simplement "effacer" l'intégralité MessageQueuede a Handler, même si vous faites une demande pour l' MessageQueueobjet qui lui est associé car les méthodes d'ajout et de suppression d'éléments sont protégées par le package (seules les classes du package android.os peuvent les appeler). Vous devrez peut-être créer une Handlersous-classe mince pour gérer une liste de Runnables au fur et à mesure de leur publication / exécution ... ou chercher un autre paradigme pour passer vos messages entre chaqueActivity

J'espère que cela pourra aider!

Devunwired
la source
Merci, je le sais. Mais j'ai beaucoup de Runnable dans de nombreuses sous-classes, et les gérer tous est un travail épique! Est-il possible de les supprimer tous, dans l'événement onStop ()?
Luke Vo
Compris, j'ai mis à jour la réponse avec un peu plus d'informations. La version courte est que vous ne pouvez pas appeler une méthode pour effacer largement la file d'attente de messages d'un gestionnaire ...
Devunwired
8

Si vous n'avez pas les références Runnable, lors du premier rappel, obtenez l'obj du message et utilisez removeCallbacksAndMessages () pour supprimer tous les rappels associés.

alphazero
la source
6

Définissez un nouveau gestionnaire et exécutable:

private Handler handler = new Handler(Looper.getMainLooper());
private Runnable runnable = new Runnable() {
        @Override
        public void run() {
            // Do what ever you want
        }
    };

Poste d'appel retardé:

handler.postDelayed(runnable, sleep_time);

Supprimez votre rappel de votre gestionnaire:

handler.removeCallbacks(runnable);
population
la source
3

Veuillez noter que l'on doit définir une Handleret une Runnableportée en classe, afin qu'il soit créé une fois. removeCallbacks(Runnable)fonctionne correctement, sauf si on les définit plusieurs fois. Veuillez consulter les exemples suivants pour une meilleure compréhension:

Manière incorrecte:

    public class FooActivity extends Activity {
           private void handleSomething(){
                Handler handler = new Handler();
                Runnable runnable = new Runnable() {
                   @Override
                   public void run() {
                      doIt();
                  }
               };
              if(shouldIDoIt){
                  //doIt() works after 3 seconds.
                  handler.postDelayed(runnable, 3000);
              } else {
                  handler.removeCallbacks(runnable);
              }
           }

          public void onClick(View v){
              handleSomething();
          }
    } 

Si vous appelez onClick(..)méthode, vous n'arrêtez jamais d' doIt()appeler méthode avant d'appeler. Parce que chaque fois crée new Handleret new Runnableinstances. De cette façon, vous avez perdu les références nécessaires qui appartiennent au gestionnaire et aux instances exécutables .

Façon correcte:

 public class FooActivity extends Activity {
        Handler handler = new Handler();
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                doIt();
            }
        };
        private void handleSomething(){
            if(shouldIDoIt){
                //doIt() works after 3 seconds.
                handler.postDelayed(runnable, 3000);
            } else {
                handler.removeCallbacks(runnable);
            }
       }

       public void onClick(View v){
           handleSomething();
       }
 } 

De cette façon, vous ne perdez pas les références réelles et removeCallbacks(runnable)fonctionne correctement.

Phrase clé est que « les définissent comme globale dans votre Activityou Fragmentce que vous utilisez » .

oguzhan
la source
1

Comme josh527dit, handler.removeCallbacksAndMessages(null);peut fonctionner.
Mais pourquoi?
Si vous regardez le code source, vous pouvez le comprendre plus clairement. Il existe 3 types de méthode pour supprimer les rappels / messages du gestionnaire (MessageQueue):

  1. supprimer par rappel (et par jeton)
  2. supprimer par message.what (et jeton)
  3. supprimer par jeton

Handler.java (laisser une méthode de surcharge)

/**
 * Remove any pending posts of Runnable <var>r</var> with Object
 * <var>token</var> that are in the message queue.  If <var>token</var> is null,
 * all callbacks will be removed.
 */
public final void removeCallbacks(Runnable r, Object token)
{
    mQueue.removeMessages(this, r, token);
}

/**
 * Remove any pending posts of messages with code 'what' and whose obj is
 * 'object' that are in the message queue.  If <var>object</var> is null,
 * all messages will be removed.
 */
public final void removeMessages(int what, Object object) {
    mQueue.removeMessages(this, what, object);
}

/**
 * Remove any pending posts of callbacks and sent messages whose
 * <var>obj</var> is <var>token</var>.  If <var>token</var> is null,
 * all callbacks and messages will be removed.
 */
public final void removeCallbacksAndMessages(Object token) {
    mQueue.removeCallbacksAndMessages(this, token);
}

MessageQueue.java fait le vrai travail:

void removeMessages(Handler h, int what, Object object) {
    if (h == null) {
        return;
    }

    synchronized (this) {
        Message p = mMessages;

        // Remove all messages at front.
        while (p != null && p.target == h && p.what == what
               && (object == null || p.obj == object)) {
            Message n = p.next;
            mMessages = n;
            p.recycleUnchecked();
            p = n;
        }

        // Remove all messages after front.
        while (p != null) {
            Message n = p.next;
            if (n != null) {
                if (n.target == h && n.what == what
                    && (object == null || n.obj == object)) {
                    Message nn = n.next;
                    n.recycleUnchecked();
                    p.next = nn;
                    continue;
                }
            }
            p = n;
        }
    }
}

void removeMessages(Handler h, Runnable r, Object object) {
    if (h == null || r == null) {
        return;
    }

    synchronized (this) {
        Message p = mMessages;

        // Remove all messages at front.
        while (p != null && p.target == h && p.callback == r
               && (object == null || p.obj == object)) {
            Message n = p.next;
            mMessages = n;
            p.recycleUnchecked();
            p = n;
        }

        // Remove all messages after front.
        while (p != null) {
            Message n = p.next;
            if (n != null) {
                if (n.target == h && n.callback == r
                    && (object == null || n.obj == object)) {
                    Message nn = n.next;
                    n.recycleUnchecked();
                    p.next = nn;
                    continue;
                }
            }
            p = n;
        }
    }
}

void removeCallbacksAndMessages(Handler h, Object object) {
    if (h == null) {
        return;
    }

    synchronized (this) {
        Message p = mMessages;

        // Remove all messages at front.
        while (p != null && p.target == h
                && (object == null || p.obj == object)) {
            Message n = p.next;
            mMessages = n;
            p.recycleUnchecked();
            p = n;
        }

        // Remove all messages after front.
        while (p != null) {
            Message n = p.next;
            if (n != null) {
                if (n.target == h && (object == null || n.obj == object)) {
                    Message nn = n.next;
                    n.recycleUnchecked();
                    p.next = nn;
                    continue;
                }
            }
            p = n;
        }
    }
}
JamesRobert
la source