¿Cómo eliminar todas las devoluciones de llamada de un controlador?

222

Tengo un controlador de mi sub-actividad que fue llamado por la actividad principal . Las subclases utilizan este postDelaycontrolador para algunos Runnables, y no puedo administrarlos. Ahora, en el onStopcaso, necesito eliminarlos antes de terminar la Actividad (de alguna manera llamé finish(), pero todavía llama una y otra vez). ¿Hay alguna forma de eliminar todas las devoluciones de llamada de un controlador?

Luke Vo
fuente

Respuestas:

522

En mi experiencia, llamar a esto funcionó muy bien.

handler.removeCallbacksAndMessages(null);

En los documentos para removeCallbacksAndMessages dice ...

Elimine cualquier publicación pendiente de devoluciones de llamada y mensajes enviados cuyo obj es token. Si el token es null, todas las devoluciones de llamada y mensajes serán eliminados.

josh527
fuente
2
@Malachiasz Creo que lo usaría en onStop o onPause, para asegurarme de que no se manejen mensajes después de que la actividad haya perdido el foco. Pero depende de lo que deba hacerse cuando se active la devolución de llamada / mensaje
Boy
1
Creo que he visto NPE antes en algunos teléfonos al hacer esto, pero ha pasado un tiempo.
Matt Wolfe
3
He tenido algunos problemas con removeCallbacksAndMessages(null)no eliminar algunas de mis devoluciones de llamada. Cuando quisiera dejar de recibir devoluciones de llamada, llamaría handler.removeCallbacksAndMessages(null)y establecería mi controlador en nulo, pero como todavía recibiría la devolución de llamada, me encontraría con un NPE cuando quisiera hacer un bucle handler.postDelayed().
Snaker
@Snaker ¿Ya resolvió su problema? Tengo el mismo problema donde se llama a Handler.Callback incluso después de eliminar las devoluciones de llamada y los mensajes estableciendo nulo.
ShrimpCrackers
1
@ShrimpCrackers Descubrí que mantener una instancia de tu ejecutable y usarlo yourHandler.removeCallbacks(yourRunnable)era lo más confiable. Sigo usando eso hoy.
Snaker
19

Para cualquier Runnableinstancia específica , llame Handler.removeCallbacks(). Tenga en cuenta que usa la Runnableinstancia en sí para determinar qué devoluciones de llamada se deben anular, por lo que si está creando una nueva instancia cada vez que se realiza una publicación, debe asegurarse de tener referencias a la exacta Runnablepara cancelar. Ejemplo:

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

Puede llamar myHandler.postDelayed(myRunnable, x)para publicar otra devolución de llamada en la cola de mensajes en otros lugares de su código y eliminar todas las devoluciones de llamada pendientes conmyHandler.removeCallbacks(myRunnable)

Desafortunadamente, no puede simplemente "borrar" todo MessageQueuepara un Handler, incluso si realiza una solicitud para el MessageQueueobjeto asociado con él porque los métodos para agregar y eliminar elementos están protegidos por el paquete (solo las clases dentro del paquete android.os pueden llamarlos). Puede que tenga que crear una Handlersubclase delgada para administrar una lista de Runnablecorreos electrónicos a medida que se publican / ejecutan ... o mire otro paradigma para pasar sus mensajes entre cadaActivity

¡Espero que ayude!

Devunwired
fuente
Gracias, lo se. ¡Pero tengo muchos Runnable en muchas subclases, y administrarlos todos es un trabajo épico! ¿Hay alguna forma de eliminarlos todos, en el evento onStop ()?
Luke Vo
Entendido, actualicé la respuesta con un poco más de información. Versión corta es que no se puede llamar a un método para limpiar ampliamente cola de mensajes del trabajador de ...
Devunwired
8

Si no tiene las referencias Runnable, en la primera devolución de llamada, obtenga el obj del mensaje y use removeCallbacksAndMessages () para eliminar todas las devoluciones de llamada relacionadas.

alphazero
fuente
6

Definir un nuevo controlador y ejecutable:

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

Mensaje de llamada retrasado:

handler.postDelayed(runnable, sleep_time);

Elimine su devolución de llamada de su controlador:

handler.removeCallbacks(runnable);
población
fuente
3

Tenga en cuenta que uno debe definir un Handlery un Runnablealcance en clase, para que se cree una vez. removeCallbacks(Runnable)funciona correctamente a menos que uno los defina varias veces. Mire los siguientes ejemplos para una mejor comprensión:

Forma incorrecta:

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

Si llama al onClick(..)método, nunca deja de doIt()llamar al método antes de que llame. Porque cada vez crea new Handlere new Runnableinstancias. De esta manera, usted perdió las referencias necesarias que pertenecen al manejador y ejecutables casos.

Forma correcta:

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

De esta manera, no pierde referencias reales y removeCallbacks(runnable)funciona con éxito.

La oración clave es que "defínalos como globales en tu Activityo Fragmentlo que usas" .

oguzhan
fuente
1

Como se josh527dijo, handler.removeCallbacksAndMessages(null);puede funcionar.
¿Pero por qué?
Si echas un vistazo al código fuente, puedes entenderlo más claramente. Hay 3 tipos de métodos para eliminar devoluciones de llamadas / mensajes del controlador (MessageQueue):

  1. eliminar por devolución de llamada (y token)
  2. eliminar por mensaje.what (y token)
  3. eliminar por token

Handler.java (deje algún 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 hace el trabajo 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
fuente