Как удалить все обратные вызовы из обработчика?

222

У меня есть обработчик из моего подкласса, который был вызван основным делом . Этот обработчик используется подклассами для postDelayнекоторых Runnables, и я не могу ими управлять. Теперь, в onStopслучае, мне нужно удалить их перед завершением Деятельности (как-то я звонил finish(), но он все равно звонил снова и снова). Есть ли способ удалить все обратные вызовы из обработчика?

Люк Во
источник

Ответы:

522

По моему опыту звонки это работало отлично!

handler.removeCallbacksAndMessages(null);

В документах для removeCallbacksAndMessages говорится ...

Удалите все ожидающие сообщения обратных вызовов и отправленные сообщения, чей объект является токеном. Если токен есть null, все обратные вызовы и сообщения будут удалены.

josh527
источник
2
@Malachiasz Я думаю, я бы использовал его в onStop или onPause, чтобы убедиться, что никакие сообщения не обрабатываются после потери активности. Но зависит от того, что должно быть сделано, когда обратный вызов / сообщение запущено
Мальчик
1
Я полагаю, что я видел NPE раньше на некоторых телефонах, когда делал это, но это было некоторое время.
Мэтт Вулф
3
У меня были некоторые проблемы removeCallbacksAndMessages(null), не удалял некоторые из моих обратных вызовов. Когда я хотел бы прекратить получать handler.removeCallbacksAndMessages(null)обратные вызовы, я звонил и устанавливал свой обработчик на ноль, но, так как я все еще получал обратный вызов, я столкнулся бы с NPE, когда захотел бы выполнить цикл handler.postDelayed().
Snaker
@ Snaker Вы уже решили свою проблему? У меня та же проблема, когда Handler.Callback вызывается даже после удаления обратных вызовов и сообщений путем установки значения NULL.
Креветочные крекеры
1
@ShrimpCrackers Я обнаружил, что хранение экземпляра вашего runnable и использование 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для a Handler, даже если вы делаете запрос для MessageQueueобъекта, связанного с ним, потому что методы добавления и удаления элементов защищены пакетами (их могут вызывать только классы в пакете android.os). Возможно, вам придется создать тонкий Handlerподкласс, чтобы управлять списком объектов, Runnableкогда они публикуются / выполняются ... или искать другую парадигму для передачи ваших сообщений между каждымActivity

Надеюсь, это поможет!

Devunwired
источник
Спасибо, я это знаю. Но у меня много Runnable во многих подклассах, и управлять ими - это эпическая работа! Есть ли способ удалить их все в событии onStop ()?
Люк Во
Понял, я обновил ответ немного больше информации. Короткая версия: вы не можете вызвать метод для широкой очистки очереди сообщений обработчика ...
Devunwired
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);
savepopulation
источник
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том, что вы используете» .

Oguzhan
источник
1

Как josh527сказано, handler.removeCallbacksAndMessages(null);может работать.
Но почему?
Если вы посмотрите на исходный код, вы сможете понять его более четко. Существует 3 типа методов для удаления обратных вызовов / сообщений из обработчика (MessageQueue):

  1. удалить с помощью обратного вызова (и токена)
  2. удалить с помощью message.what (и токен)
  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;
        }
    }
}
JamesRobert
источник