Jak usunąć wszystkie wywołania zwrotne z programu obsługi?

222

Mam moduł obsługi z mojego poddziałania, który został wywołany przez główne działanie . Ten moduł obsługi jest używany przez podklasy postDelayniektórych Runnables i nie mogę nimi zarządzać. Teraz, w onStopprzypadku, muszę je usunąć przed zakończeniem działania (jakoś zadzwoniłem finish(), ale wciąż dzwoni ponownie). Czy istnieje sposób na usunięcie wszystkich wywołań zwrotnych z modułu obsługi?

Luke Vo
źródło

Odpowiedzi:

521

Z mojego doświadczenia, nazywanie tego działało świetnie!

handler.removeCallbacksAndMessages(null);

W dokumentacji dotyczącej removeCallbacksAndMessages jest napisane ...

Usuń wszystkie oczekujące posty oddzwonień i wysłane wiadomości, których obj jest tokenem. Jeśli token jest null, wszystkie połączenia zwrotne i wiadomości zostaną usunięte.

josh527
źródło
2
@Malachiasz Myślę, że użyłbym go w trybie OnStop lub onPause, aby upewnić się, że żadne wiadomości nie będą obsługiwane po utracie aktywności. Ale zależy od tego, co należy zrobić, gdy zostanie oddzwonione / wiadomość
Boy
1
Wydaje mi się, że widziałem wcześniej NPE na niektórych telefonach, ale to już dawno.
Matt Wolfe
3
Miałem pewne problemy z tym, removeCallbacksAndMessages(null)że nie usuną niektórych moich oddzwonień. Gdybym chciał przestać odbierać wywołania zwrotne, zadzwoniłem handler.removeCallbacksAndMessages(null)i ustawiłem mój moduł obsługi na zero, ale ponieważ nadal otrzymywałbym wywołanie zwrotne, napotkałem NPE, gdy chciałbym zapętlić handler.postDelayed().
Snaker
@Snaker Czy rozwiązałeś już swój problem? Mam ten sam problem z wywoływaniem funkcji Handler.Callback nawet po usunięciu wywołań zwrotnych i wiadomości przez ustawienie wartości null.
ShrimpCrackers
1
@ShrimpCrackers Dowiedziałem się, że utrzymywanie instancji twojego runnable i używanie yourHandler.removeCallbacks(yourRunnable)było najbardziej niezawodne. Nadal tego używam dzisiaj.
Snaker
19

W przypadku dowolnej konkretnej Runnableinstancji zadzwoń Handler.removeCallbacks(). Zauważ, że korzysta on z Runnablesamej instancji, aby określić, które oddzwaniania należy wyrejestrować, więc jeśli tworzysz nową instancję za każdym razem, gdy publikowany jest post, musisz upewnić się, że masz odniesienia do dokładnego Runnableanulowania. Przykład:

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

Możesz zadzwonić, myHandler.postDelayed(myRunnable, x)aby opublikować kolejne wywołanie zwrotne w kolejce wiadomości w innych miejscach kodu i usunąć wszystkie oczekujące wywołania zwrotne za pomocąmyHandler.removeCallbacks(myRunnable)

Niestety, nie można po prostu „wyczyścić” Cały MessageQueuedla Handler, nawet jeśli się z wnioskiem o MessageQueueobiektu związanego z nim ponieważ metody dodawania i usuwania elementów pakietów są zabezpieczone (tylko klas w pakiecie android.os można je nazwać). Być może będziesz musiał utworzyć cienką Handlerpodklasę, aby zarządzać listą Runnables, gdy są one publikowane / wykonywane ... lub spójrz na inny paradygmat przekazywania wiadomości między nimiActivity

Mam nadzieję, że to pomoże!

Devunwired
źródło
Dzięki, wiem o tym. Ale mam wiele Runnable w wielu podklasach, a zarządzanie nimi wszystkimi to epicka praca! Czy istnieje możliwość ich usunięcia w zdarzeniu onStop ()?
Luke Vo
Zrozumiałem, zaktualizowałem odpowiedź nieco więcej informacji. Krótka wersja jest nie można wywołać metodę szeroko jasny komunikat kolejki Handler za ...
Devunwired
8

Jeśli nie masz odwołań Runnable, przy pierwszym wywołaniu zwrotnym pobierz obiekt obj wiadomości i użyj removeCallbacksAndMessages (), aby usunąć wszystkie powiązane wywołania zwrotne.

alphazero
źródło
6

Zdefiniuj nowy moduł obsługi i uruchom:

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

Opóźniony post połączenia:

handler.postDelayed(runnable, sleep_time);

Usuń połączenie zwrotne z programu obsługi:

handler.removeCallbacks(runnable);
populacja
źródło
3

Należy pamiętać, że należy zdefiniować zakres A Handleroraz A Runnablew klasie, aby był on utworzony raz. removeCallbacks(Runnable)działa poprawnie, chyba że zdefiniuje się je wiele razy. Aby lepiej zrozumieć, spójrz na następujące przykłady:

Niepoprawny sposób:

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

Jeśli wywołasz onClick(..)metodę, nigdy nie przestaniesz doIt()wywoływać metody przed jej wywołaniem. Ponieważ za każdym razem tworzy new Handleri new Runnableinstancje. W ten sposób utraciłeś niezbędne referencje, które należą do instancji procedury obsługi i uruchamiania .

Właściwa droga :

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

W ten sposób nie straciłeś faktycznych referencji i removeCallbacks(runnable)działa z powodzeniem.

Kluczowe zdanie brzmi: „zdefiniuj je jako globalne w swoim Activitylub w Fragmenttym, czego używasz” .

oguzhan
źródło
1

Jak josh527powiedziano, handler.removeCallbacksAndMessages(null);może działać.
Ale dlaczego?
Jeśli spojrzysz na kod źródłowy, możesz go lepiej zrozumieć. Istnieją 3 rodzaje metod usuwania wywołań zwrotnych / komunikatów z modułu obsługi (MessageQueue):

  1. usuń przez oddzwonienie (i token)
  2. usuń przez message.what (i token)
  3. usuń tokenem

Handler.java (pozostaw metodę przeciążenia)

/**
 * 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 wykona prawdziwą pracę:

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
źródło