Як видалити всі зворотні дзвінки з обробника?


222

У мене є обробник з моєї суб-діяльності, яку викликали головною активністю . Цей обробник використовується postDelayпідкласами для деяких Runnables, і я не можу ними керувати. Тепер, у onStopвипадку, мені потрібно видалити їх, перш ніж закінчити Діяльність (якось я зателефонував finish(), але він все ще дзвонить знову і знову). Чи все-таки потрібно видалити всі зворотні дзвінки з обробника?

Відповіді:


522

На моєму досвіді заклик до цього працював чудово!

handler.removeCallbacksAndMessages(null);

У документах для removeCallbacksAndMessages написано ...

Видаліть очікувані повідомлення про зворотні дзвінки та надіслані повідомлення, об'єкт яких є маркером. Якщо маркер є null, всі зворотні дзвінки та повідомлення будуть видалені.


2
@Malachiasz Я думаю, що я використовував би його в onStop або onPause, щоб переконатися, що повідомлення не обробляються після того, як активність втратила фокус. Але залежить від того, що потрібно зробити, коли зворотний виклик / повідомлення буде знято
Хлопчик

1
Я вважаю, що я бачив NPE раніше на деяких телефонах, коли це робив, але це вже давно.
Метт Вулф

3
У мене виникли проблеми з тим, що removeCallbacksAndMessages(null)б не видалити деякі мої зворотні дзвінки. Коли я хотів би перестати отримувати handler.removeCallbacksAndMessages(null)зворотні дзвінки, я б зателефонував і встановив обробник на нуль, але оскільки я все-таки отримав зворотний виклик, я зіткнувся з NPE, коли я хотів би пройти цикл handler.postDelayed().
Snaker

@Snaker Ви вже вирішили свою проблему? У мене така ж проблема, коли Handler.Callback викликається навіть після видалення зворотних дзвінків та повідомлень, встановивши null.
ShrimpCrackers

1
@ShrimpCrackers Я з’ясував, що збереження екземпляра вашого запуску та використання yourHandler.removeCallbacks(yourRunnable)було найбільш надійним. Все ще використовую це сьогодні.
Snaker

19

Для будь-якого конкретного Runnableвипадку зателефонуйте Handler.removeCallbacks(). Зауважте, що він використовує сам Runnableекземпляр, щоб визначити, які зворотні дзвінки потрібно відреєструвати, тому якщо ви створюєте новий екземпляр щоразу, коли публікується повідомлення, вам потрібно переконатися, що ви маєте посилання на точне Runnableскасування. Приклад:

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

Ви можете зателефонувати, myHandler.postDelayed(myRunnable, x)щоб опублікувати ще один зворотний дзвінок у черзі повідомлень в інших місцях вашого коду та видалити всі очікувані зворотні дзвінки за допомогоюmyHandler.removeCallbacks(myRunnable)

На жаль, ви не можете просто "очистити" ціле MessageQueueдля а Handler, навіть якщо ви зробите запит на MessageQueueпов'язаний з ним об'єкт, оскільки методи додавання та видалення елементів захищені пакетом (лише класи в пакеті android.os можуть викликати їх). Можливо, вам доведеться створити тонкий Handlerпідклас для управління списком Runnables, коли вони розміщені / виконані ... або переглянути іншу парадигму для передачі ваших повідомлень між кожнимActivity

Сподіваюся, що це допомагає!


Дякую, я це знаю. Але у мене є багато Runnable у багатьох підкласах, і керувати ними все - епічна робота! Чи все-таки потрібно видалити їх усіх у події onStop ()?
Лука Во

Зрозумів, я оновив відповідь трохи більше інформації. Коротка версія - це те, що ви не можете зателефонувати за методом широкого очищення черги повідомлень обробника ...
Devunwired

8

Якщо у вас немає посилань на Runnable, під час першого зворотного дзвінка отримайте obj цього повідомлення та використовуйте deleteCallbacksAndMessages (), щоб видалити всі відповідні зворотні дзвінки.


6

Визначте новий обробник та виконується:

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

Запис дзвінка затриманий:

handler.postDelayed(runnable, sleep_time);

Видаліть зворотний дзвінок зі свого обробника:

handler.removeCallbacks(runnable);

3

Зверніть увагу, що слід визначити a Handlerі a Runnableу класі класу, щоб він був створений один раз. removeCallbacks(Runnable)працює правильно, якщо не визначити їх кілька разів. Перегляньте наступні приклади для кращого розуміння:

Неправильний спосіб:

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

Якщо ви викликаєте onClick(..)метод, ви ніколи не зупиняєте doIt()виклик методу перед тим, як викликати. Тому що кожен раз створює new Handlerта new Runnableінстанції. Таким чином, ви втратили необхідні посилання, що належать до оброблювальних та запущених екземплярів.

Правильний шлях:

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

Таким чином, ви не втратили фактичних посилань і removeCallbacks(runnable)працює успішно.

Ключовим реченням є те, що "визначте їх як глобальні у вашому Activityабо Fragmentтому, що ви використовуєте" .


1

Як josh527сказано, handler.removeCallbacksAndMessages(null);може працювати.
Але чому?
Якщо ви подивитесь на вихідний код, ви можете зрозуміти його більш чітко. Існує 3 типу способу видалення зворотних дзвінків / повідомлень з обробника (черга повідомлень):

  1. видалити за допомогою зворотного дзвінка (і маркера)
  2. видалити повідомленням. Що (та маркер)
  3. видалити за допомогою маркера

Handler.java (залиште деякий метод перевантаження)

/**
 * 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 виконати справжню роботу:

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;
        }
    }
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.