Come rimuovere tutti i callback da un gestore?


222

Ho un Handler dal mio sub-Attività che è stato chiamato dal principale attività . Questo gestore viene utilizzato dalle sottoclassi di postDelayalcuni Runnable e non riesco a gestirli. Ora, nel onStopcaso, devo rimuoverli prima di terminare l'attività (in qualche modo ho chiamato finish(), ma continua a chiamare ancora e ancora). Esiste un modo per rimuovere tutti i callback da un gestore?

Risposte:


522

Nella mia esperienza chiamata questo ha funzionato alla grande!

handler.removeCallbacksAndMessages(null);

Nei documenti per removeCallbacksAndMessages dice ...

Rimuovere eventuali post in sospeso di callback e messaggi inviati il ​​cui oggetto è token. Se il token è null, tutti i callback e i messaggi verranno rimossi.


2
@Malachiasz Penso che lo userei in onStop o onPause, per assicurarmi che nessun messaggio venga gestito dopo che l'attività ha perso il focus. Ma dipende da cosa deve essere fatto quando viene attivato il callback / messaggio
Boy

1
Credo di aver visto NPE prima su alcuni telefoni mentre lo facevo, ma è passato un po 'di tempo.
Matt Wolfe,

3
Ho avuto alcuni problemi con removeCallbacksAndMessages(null)non rimuovere alcuni dei miei callback. Quando vorrei smettere di ricevere i callback, chiamavo handler.removeCallbacksAndMessages(null)e impostavo il mio gestore su null, ma dato che avrei ancora ricevuto il callback, avrei incontrato un NPE quando avrei voluto collegarmi handler.postDelayed().
Snaker,

@Snaker Hai già risolto il tuo problema? Sto riscontrando lo stesso problema in cui Handler.Callback viene chiamato anche dopo aver rimosso callback e messaggi impostando null.
ShrimpCrackers

1
@ShrimpCrackers Ho scoperto che mantenere un'istanza del tuo runnable e usare yourHandler.removeCallbacks(yourRunnable)era il più affidabile. Lo uso ancora oggi.
Snaker,

19

Per qualsiasi Runnableistanza specifica , chiama Handler.removeCallbacks(). Nota che utilizza l' Runnableistanza stessa per determinare quali callback Runnableannullare la registrazione, quindi se stai creando una nuova istanza ogni volta che viene pubblicato un post, devi assicurarti di avere riferimenti all'esatto da annullare. Esempio:

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

Puoi chiamare myHandler.postDelayed(myRunnable, x)per pubblicare un altro callback nella coda messaggi in altri punti del codice e rimuovere tutti i callback in sospeso conmyHandler.removeCallbacks(myRunnable)

Sfortunatamente, non puoi semplicemente "cancellare" il tutto MessageQueueper a Handler, anche se fai una richiesta per l' MessageQueueoggetto ad esso associato perché i metodi per aggiungere e rimuovere oggetti sono protetti da pacchetti (solo le classi all'interno del pacchetto android.os possono chiamarli). Potrebbe essere necessario creare una Handlersottoclasse sottile per gestire un elenco di Runnablemessaggi di posta elettronica come vengono pubblicati / eseguiti ... o guardare un altro paradigma per passare i messaggi tra ogniActivity

Spero che aiuti!


Grazie, lo so. Ma ho un sacco di Runnable in molte sottoclassi e gestirle tutte è un'opera epica! Esiste un modo per rimuoverli tutti, nell'evento onStop ()?
Luke Vo,

Capito, ho aggiornato la risposta con un po 'più di informazioni. La versione breve è che non puoi chiamare un metodo per cancellare ampiamente la coda dei messaggi di un gestore ...
Devunwired


6

Definire un nuovo gestore ed eseguibile:

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

Post di chiamata in ritardo:

handler.postDelayed(runnable, sleep_time);

Rimuovi il callback dal gestore:

handler.removeCallbacks(runnable);

3

Si noti che è necessario definire un ambito a Handlere a Runnablein class, in modo che venga creato una volta. removeCallbacks(Runnable)funziona correttamente a meno che uno non li definisca più volte. Si prega di guardare i seguenti esempi per una migliore comprensione:

Modo errato:

    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();
          }
    } 

Se chiami il onClick(..)metodo, non fermi mai la doIt()chiamata del metodo prima di chiamare. Perché ogni volta crea new Handlere new Runnableistanze. In questo modo, hai perso i riferimenti necessari che appartengono al gestore e alle istanze eseguibili .

Modo corretto :

 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();
       }
 } 

In questo modo, non hai perso riferimenti reali e removeCallbacks(runnable)funziona correttamente.

La frase chiave è che "definiscili come globali nel tuo Activityo in Fragmentquello che usi" .


1

Come josh527detto, handler.removeCallbacksAndMessages(null);può funzionare.
Ma perché?
Se dai un'occhiata al codice sorgente, puoi capirlo più chiaramente. Esistono 3 tipi di metodo per rimuovere callback / messaggi dal gestore (MessageQueue):

  1. rimuovi per callback (e token)
  2. rimuovere da message.what (e token)
  3. rimuovi tramite token

Handler.java (lasciare un metodo di sovraccarico)

/**
 * 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 fa il vero lavoro:

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;
        }
    }
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.