Como remover todos os retornos de chamada de um manipulador?

222

Eu tenho um manipulador da minha sub-atividade que foi chamado pela atividade principal . Este manipulador é usado por subclasses para postDelayalguns executáveis, e eu não posso gerenciá-los. Agora, no onStopcaso, preciso removê-los antes de terminar a Atividade (de alguma forma eu liguei finish(), mas ainda assim chamo novamente e novamente). Existe alguma maneira de remover todos os retornos de chamada de um manipulador?

Luke Vo
fonte

Respostas:

522

Na minha experiência, chamar isso funcionou muito bem!

handler.removeCallbacksAndMessages(null);

Nos documentos para removeCallbacksAndMessages, ele diz ...

Remova quaisquer postagens pendentes de retornos de chamada e mensagens enviadas cujo objetivo seja um token. Se o token for null, todos os retornos de chamada e mensagens serão removidos.

josh527
fonte
2
@ Malachiasz Acho que o usaria no onStop ou onPause, para garantir que nenhuma mensagem seja tratada após a atividade ter perdido o foco. Mas depende do que precisa ser feito quando o retorno de chamada / mensagem é disparado
Boy
1
Acredito que já vi NPE antes em alguns telefones ao fazer isso, mas já faz um tempo.
Matt Wolfe
3
Eu tive alguns problemas com a removeCallbacksAndMessages(null)remoção de alguns dos meus retornos de chamada. Quando eu gostaria de parar de receber handler.removeCallbacksAndMessages(null)retornos de chamada, chamava e definia meu manipulador como nulo, mas como ainda recebia o retorno, encontraria um NPE quando gostaria de fazer um loop handler.postDelayed().
Snaker 23/05
@ Snnaker Você já resolveu seu problema? Estou tendo o mesmo problema em que Handler.Callback está sendo chamado, mesmo depois de remover retornos de chamada e mensagens, definindo nulo.
ShrimpCrackers
1
@ShrimpCrackers Descobri que manter uma instância do seu executável e usar yourHandler.removeCallbacks(yourRunnable)era a mais confiável. Ainda usando isso hoje.
Snaker
19

Para qualquer Runnableinstância específica , ligue Handler.removeCallbacks(). Observe que ele usa a Runnableprópria instância para determinar quais retornos de chamada devem ser cancelados, portanto, se você estiver criando uma nova instância cada vez que uma postagem for feita, verifique se você tem referências exatas Runnablepara cancelar. Exemplo:

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

Você pode ligar myHandler.postDelayed(myRunnable, x)para postar outro retorno de chamada na fila de mensagens em outros lugares do seu código e remover todos os retornos de chamada pendentes commyHandler.removeCallbacks(myRunnable)

Infelizmente, você não pode simplesmente "limpar" o todo MessageQueuepara a Handler, mesmo que faça uma solicitação para o MessageQueueobjeto associado a ele, porque os métodos para adicionar e remover itens são protegidos por pacote (somente as classes do pacote android.os podem chamá-lo). Pode ser necessário criar uma Handlersubclasse fina para gerenciar uma lista de Runnables conforme eles são publicados / executados ... ou observar outro paradigma para passar suas mensagens entre cadaActivity

Espero que ajude!

Devunwired
fonte
Obrigado, eu sei disso. Mas eu tenho muitos Runnable em muitas subclasses, e gerenciar todos eles é um trabalho épico! Existe alguma maneira de removê-los todos, no evento onStop ()?
Luke Vo
Entendido, atualizei a resposta com um pouco mais de informação. Versão curta é que você não pode chamar um método para limpar amplamente fila de mensagens de um Handler ...
Devunwired
8

Se você não tiver as referências Runnable, no primeiro retorno de chamada, obtenha o objetivo da mensagem e use removeCallbacksAndMessages () para remover todos os retornos de chamada relacionados.

alphazero
fonte
6

Defina um novo manipulador e executável:

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

Mensagem atrasada:

handler.postDelayed(runnable, sleep_time);

Remova seu retorno de chamada do manipulador:

handler.removeCallbacks(runnable);
savepopulation
fonte
3

Observe que deve-se definir a Handlere a Runnableno escopo da classe, para que seja criado uma vez.removeCallbacks(Runnable)funciona corretamente, a menos que alguém os defina várias vezes. Veja os exemplos a seguir para entender melhor:

Maneira incorreta:

    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 você chamar o onClick(..)método, nunca interrompa a doIt()chamada do método antes que ela seja chamada. Porque cada vez que cria new Handlere new Runnableinstâncias. Dessa forma, você perdeu as referências necessárias que pertencem ao manipulador e executável instâncias .

Maneira correta :

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

Dessa forma, você não perde referências reais e removeCallbacks(runnable) trabalha com sucesso.

A frase-chave é que "defina-os como globais no seu Activityou no Fragmentque você usa" .

oguzhan
fonte
1

Como josh527dito, handler.removeCallbacksAndMessages(null);pode funcionar.
Mas por que?
Se você der uma olhada no código-fonte, poderá entendê-lo mais claramente. Existem três tipos de método para remover retornos de chamada / mensagens do manipulador (o MessageQueue):

  1. remover por retorno de chamada (e token)
  2. remover por message.what (e token)
  3. remover por token

Handler.java (deixe algum método de sobrecarga)

/**
 * 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 faz o trabalho real:

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
fonte