Wie entferne ich alle Rückrufe von einem Handler?

222

Ich habe einen Handler von meiner Unteraktivität, die von der Haupt genannt wurde Aktivität . Dieser Handler wird von Unterklassen für postDelayeinige Runnables verwendet, und ich kann sie nicht verwalten. Jetzt muss onStopich sie für den Fall entfernen, bevor ich die Aktivität beende (irgendwie habe ich angerufen finish(), aber es wird immer wieder aufgerufen ). Gibt es überhaupt eine Möglichkeit, alle Rückrufe von einem Handler zu entfernen?

Luke Vo
quelle

Antworten:

522

Nach meiner Erfahrung hat es super geklappt!

handler.removeCallbacksAndMessages(null);

In den Dokumenten für removeCallbacksAndMessages heißt es ...

Entfernen Sie alle ausstehenden Posts von Rückrufen und gesendeten Nachrichten, deren Objekt ein Token ist. Wenn dies der Fall ist null, werden alle Rückrufe und Nachrichten entfernt.

josh527
quelle
2
@Malachiasz Ich denke, ich würde es in onStop oder onPause verwenden, um sicherzustellen, dass keine Nachrichten verarbeitet werden, nachdem die Aktivität den Fokus verloren hat. Aber hängt davon ab, was zu tun ist, wenn der Rückruf / die Nachricht ausgelöst wird
Boy
1
Ich glaube, ich habe NPE schon einmal auf einigen Handys gesehen, aber es ist schon eine Weile her.
Matt Wolfe
3
Ich hatte einige Probleme removeCallbacksAndMessages(null)damit, einige meiner Rückrufe nicht zu entfernen. Wenn ich handler.removeCallbacksAndMessages(null)keine Rückrufe mehr erhalten möchte, rufe ich an und setze meinen Handler auf null. Da ich den Rückruf jedoch weiterhin erhalten würde, würde ich auf eine NPE stoßen, wenn ich eine Schleife durchführen möchte handler.postDelayed().
Snaker
@Snaker Hast du dein Problem schon gelöst? Ich habe das gleiche Problem, bei dem Handler.Callback aufgerufen wird, auch nachdem Rückrufe und Nachrichten durch Setzen von null entfernt wurden.
ShrimpCrackers
1
@ ShrimpCrackers Ich habe herausgefunden, dass yourHandler.removeCallbacks(yourRunnable)es am zuverlässigsten ist , eine Instanz Ihres Laufprogramms zu halten und zu verwenden . Ich benutze das heute noch.
Snaker
19

RunnableRufen Sie für eine bestimmte Instanz an Handler.removeCallbacks(). Beachten Sie, dass die RunnableInstanz selbst verwendet wird, um zu bestimmen, welche Rückrufe abgemeldet werden sollen. Wenn Sie also jedes Mal, wenn ein Beitrag erstellt wird, eine neue Instanz erstellen, müssen Sie sicherstellen, dass Sie Verweise auf die genauen RunnableAbbrüche haben, die abgebrochen werden sollen. Beispiel:

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

Sie können anrufen, myHandler.postDelayed(myRunnable, x)um an anderen Stellen in Ihrem Code einen weiteren Rückruf in die Nachrichtenwarteschlange zu stellen, und alle ausstehenden Rückrufe mit entfernenmyHandler.removeCallbacks(myRunnable)

Leider können Sie nicht einfach das gesamte "löschen" MessageQueue für aHandler , selbst wenn Sie eine Anfrage für das MessageQueuezugehörige Objekt stellen, da die Methoden zum Hinzufügen und Entfernen von Elementen paketgeschützt sind (nur Klassen innerhalb des android.os-Pakets können sie aufrufen). Möglicherweise müssen Sie eine dünne HandlerUnterklasse erstellen , um eine Liste von Runnables zu verwalten, während diese veröffentlicht / ausgeführt werden ... oder ein anderes Paradigma für die Weitergabe Ihrer Nachrichten zwischen den einzelnen Klassen betrachtenActivity

Hoffentlich hilft das!

Devunwired
quelle
Danke, das weiß ich. Aber ich habe viel Runnable in vielen Unterklassen und es ist eine epische Arbeit, sie alle zu verwalten! Gibt es überhaupt eine Möglichkeit, sie alle im Ereignis onStop () zu entfernen?
Luke Vo
Verstanden habe ich die Antwort mit ein bisschen mehr Informationen aktualisiert. Kurzversion ist, dass Sie keine Methode aufrufen können, um die Nachrichtenwarteschlange eines
Handlers
8

Wenn Sie nicht über die ausführbaren Referenzen verfügen, rufen Sie beim ersten Rückruf das Objekt der Nachricht ab und entfernen Sie alle zugehörigen Rückrufe mit removeCallbacksAndMessages () .

Alphazero
quelle
6

Definieren Sie einen neuen Handler und eine ausführbare Datei:

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

Anrufpost verspätet:

handler.postDelayed(runnable, sleep_time);

Entfernen Sie Ihren Rückruf von Ihrem Handler:

handler.removeCallbacks(runnable);
Savepopulation
quelle
3

Bitte beachten Sie, dass man einen Handlerund einen RunnableIn-Class-Bereich definieren sollte , damit er einmal erstellt wird. removeCallbacks(Runnable)funktioniert korrekt, es sei denn, man definiert sie mehrmals. Bitte schauen Sie sich zum besseren Verständnis die folgenden Beispiele an:

Falscher Weg:

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

Wenn Sie onClick(..)method aufrufen , beenden Sie den Methodenaufruf nie doIt(), bevor er aufgerufen wird. Denn jedes Mal erstellt new Handlerund new RunnableInstanzen. Auf diese Weise haben Sie die erforderlichen Referenzen verloren, die zu Handler- und ausführbaren Instanzen gehören.

Der richtige Weg :

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

Auf diese Weise verlieren Sie keine tatsächlichen Referenzen und removeCallbacks(runnable) arbeiten erfolgreich.

Der Schlüsselsatz lautet: "Definieren Sie sie als global in Ihrem Activityoder dem, Fragmentwas Sie verwenden" .

oguzhan
quelle
1

Wie josh527gesagt, handler.removeCallbacksAndMessages(null);kann funktionieren.
Aber wieso?
Wenn Sie sich den Quellcode ansehen, können Sie ihn besser verstehen. Es gibt drei Arten von Methoden zum Entfernen von Rückrufen / Nachrichten aus dem Handler (die MessageQueue):

  1. durch Rückruf (und Token) entfernen
  2. durch message.what (und Token) entfernen
  3. per Token entfernen

Handler.java (Überlastungsmethode belassen)

/**
 * 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 erledigen die eigentliche Arbeit:

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
quelle