¿Cómo el Looper sabe enviar el mensaje a Handler?
La pregunta es, ¿dónde le digo a mi hilo que use mHandler para Looper ?
Gracias. Estoy utilizando el siguiente código:
class LooperThread extends Thread { public Handler mHandler; public void run() { Looper.prepare(); mHandler = new Handler() { public void handleMessage(Message msg) { // process incoming messages here } }; Looper.loop(); } }
La pregunta es, ¿dónde le digo a mi hilo que use mHandler para Looper?
Usted no necesita decirlo explícitamente, porque el sistema (framework) lo hace por usted. Al instanciar el Handler
, obtendrá automáticamente acceso a la cola de mensajes de su Thread
actual. Citando su comentario:
¿Cómo sabe el sistema enviar el mensaje al
mHandler
Handler
?
Voy a detallarlo a continuación.
Este es el constructor de android.os.Handler
en Android:
mLooper = Looper.myLooper(); if (mLooper == null) { throw new RuntimeException( "Can't create handler inside thread that has not called Looper.prepare()"); } mQueue = mLooper.mQueue;
Como puede ver, primero obtiene el Looper
de su Thread
actual. El código fuente de Looper.myLooper()
es el siguiente:
public static final Looper myLooper() { return (Looper)sThreadLocal.get(); }
Lo obtiene del almacenamiento local del hilo. Más tarde, cuando envíe un Message
con este Handler
, el Handler
realmente se establece como destinatario del Message
: así es como el Looper
sabrá dónde enviar el Message
cuando llegue. En detalles:
Cuando llama a mHandler.sendMessage()
, eventualmente este código se ejecuta (entre muchas otras líneas de código):
MessageQueue queue = mQueue; boolean sent = false; if (queue != null) { msg.target = this; // msg is your Message instance sent = queue.enqueueMessage(msg, uptimeMillis); }
Como puede ver, establece la instancia de Handler
como destino del Message
. Por lo tanto, más tarde, cuando el Message
se envía, contendrá el Handler
como su destino. Así es como el Looper
sabrá a qué Handler
debe enviarlo. En los detalles, cuando llama Looper.loop()
, lo siguiente sucede para cada una de las instancias de Message
en la cola:
msg.target.dispatchMessage(msg);
El código dispatchMessage()
es el siguiente:
public void dispatchMessage(Message msg) { if (msg.callback != null) { handleCallback(msg); } else { if (mCallback != null) { if (mCallback.handleMessage(msg)) { return; } } handleMessage(msg); } }
Observe la última handleMessage(msg)
– esto es exactamente su handleMessage(msg)
!
Para tener una mejor comprensión, cree un Thread
normal e intente crear un Handler
en el método run()
de ese subproceso. Obtendrá una RuntimeException
diciendo:
No se puede crear el controlador dentro de hilo que no ha llamado
Looper.prepare()
Ahora llamar a Looper.prepare () en el método run()
antes de crear un Handler
crearía un nuevo objeto Looper
asociado con el subproceso llamante . La fuente de su confusión es que Looper.prepare () no toma un argumento como argumento. No es necesario, ya que es un método estático, que recibe internamente el ThreadLocal
del subproceso actualmente en ejecución. Puede haber como mucho un Looper
asociado a cualquier Thread
.
Ahora, llamar a new Handler()
asociaría el nuevo objeto Handler
con el Looper
del Thread
actual llamando internamente Looper.myLooper()
. Puede crear más de un Handler
cada uno con su propia devolución de llamada en el mismo subproceso. Todos los manejadores obtendrían sus mensajes de la cola de mensajes del mismo Looper
.
No dices nada. De la documentación de Handler
:
Cada instancia de Handler se asocia con un solo subproceso y la cola de mensajes del subproceso. Cuando crea un nuevo Handler, está enlazado a la fila / cola de mensajes del subproceso que lo está creando. A partir de ese momento, entregará mensajes y ejecutables a esa cola de mensajes y los ejecutará cuando salgan de la cola de mensajes .
El controlador se vincula automáticamente a la cola de mensajes del subproceso. Sólo implementa la devolución de llamada y el sistema se ocupará de todo, es decir, despachar y procesar los mensajes. En realidad estoy de acuerdo en que, usando dos métodos estáticos como Looper.prepare()
y Looper.loop()
y automáticamente inferir las cosas, hace que el patrón se siente como la magia negra 🙂