Manejadores, MessageQueue, Looper, ¿todos ellos se ejecutan en el hilo de interfaz de usuario?

Estoy tratando de envolver mi cabeza alrededor de roscado, y sé que puedo utilizar un Handler para enviar mensajes / runnables a MessageQueue , que a su vez obtiene recogido por el Looper y enviado de vuelta al Handler para su procesamiento.

Lo que no estoy completamente seguro de es donde estas cosas están sucediendo. Más precicely: Si publico en un controlador en mi actividad, es la Activity , Handler , MessageQueue y Looper todo en ejecución en el hilo de interfaz de usuario? Si no, ¿podría alguien explicar por favor cómo todo esto viene junto? 🙂

Respuesta corta: todos corren en el mismo hilo. Si se crea una instancia desde una devolución de llamada del ciclo de vida de la Activity , todas se ejecutan en el subproceso principal de la interfaz de usuario.

Respuesta larga:

Un subproceso puede tener un Looper , que contiene un MessageQueue . Para usar esta función, tendría que crear un Looper en el subproceso actual llamando (la estática) Looper.prepare() , y luego iniciar el bucle llamando (también estático) Looper.loop() . Estos son estáticos porque sólo se supone que es un Looper por hilo.

La llamada a loop() normalmente no vuelve por algún tiempo, pero sigue tomando mensajes ("tareas", "comandos" o lo que sea que quieras llamarlos) fuera de MessageQueue y los maneja individualmente (por ejemplo, llamando a una Runnable contenida En el mensaje). Cuando no hay mensajes en la cola, el hilo se bloquea hasta que haya mensajes nuevos. Para detener un Looper , hay que llamar a quit() en él (lo que probablemente no detiene el bucle inmediatamente, sino que establece una bandera privada que se comprueba periódicamente desde el bucle, indicando que se detiene).

Sin embargo, no puede agregar mensajes a la cola directamente. En su lugar, se registra un MessageQueue.IdleHandler para esperar una llamada en queueIdle() , en la que puede decidir si desea o no algo. Todos los manejadores son llamados a su vez. (Por lo tanto, la "cola" no es realmente una cola, sino una colección de devoluciones de llamada para llamar regularmente ).

Nota con respecto al párrafo anterior: Esto yo adiviné realmente. No pude encontrar ninguna documentación sobre eso, pero tendría sentido.

Actualización: véase el comentario de ahcox y su respuesta .

Debido a que esto es un montón de trabajo, el marco proporciona la clase Handler para simplificar las cosas . Cuando crea una instancia de Handler , está (de forma predeterminada) vinculada al Looper ya conectado al subproceso actual. (El Handler sabe lo que Looper para adjuntar a porque llamamos prepare() antes, que probablemente almacenado una referencia a la Looper en un ThreadLocal .)

Con un Handler , sólo puede llamar a post() para "poner un mensaje en la cola de mensajes del hilo" (por así decirlo). El Handler se encargará de todas las cosas de callback de IdleHandler y se asegurará de que Runnable se ejecute. (También podría comprobar si ya es el momento, si ha publicado con un retraso.)

Sólo para ser claro: la única manera de hacer realidad un hilo de bucle hacer algo es enviar un mensaje a su bucle. Esto es válido hasta que llama a quit () en el looper.


Respecto al hilo de la interfaz de usuario de Android: En algún momento (probablemente antes de que se creen actividades y similares), el framework ha configurado un Looper (que contiene MessageQueue ) y lo ha iniciado. A partir de este punto, todo lo que sucede en el hilo de la interfaz de usuario es a través de ese bucle. Esto incluye la gestión del ciclo de vida de la actividad y así sucesivamente. Todas las devoluciones de llamada que anula ( onCreate() , onDestroy() …) son al menos indirectamente enviadas desde ese bucle. Usted puede ver que por ejemplo en el rastro de la pila de una excepción. (Puede intentarlo, sólo escriba int a = 1 / 0; onCreate() en algún lugar de onCreate() …)


Espero que esto tenga sentido. Lo siento por no estar claro antes.

Seguimiento de la "cómo todo se reúne" parte de la pregunta. Como usuario634618 escribió, el looper se hace cargo de un hilo, el hilo principal de la interfaz de usuario en el caso de Looper principal de una aplicación.

  • Looper.loop() extrae mensajes de su cola de mensajes. Cada mensaje tiene una referencia a un controlador asociado que se devolverá a (el miembro de destino).
  • Dentro de Looper.loop() para cada mensaje recibido de la cola:
    • public void Handler.dispatchMessage(Message msg) loop() llama a public void Handler.dispatchMessage(Message msg) utilizando el controlador que se almacena en el mensaje como su miembro de destino.
    • Si el mensaje tiene un miembro de devolución de llamada Runnable, que se ejecuta.
    • De lo contrario, si el Handler tiene un conjunto de devolución de llamada compartida, se ejecuta.
    • Else, Handler's handleMessage() se llama con el mensaje como un argumento. (Tenga en cuenta, si subclases Handler como AsyncTask hace, podría reemplazar handleMessage() como lo hace.)

En tu pregunta sobre todos los objetos que colaboran en el mismo subproceso de interfaz de usuario, se debe crear un Handler en el mismo subproceso que el Looper que enviará mensajes. Su constructor buscará el Looper actual y lo almacenará como un miembro, atando al Handler a ese Looper . También hará referencia a esa cola de mensajes de Looper directamente en su propio miembro. El Handler se puede utilizar para enviar trabajo al Looper desde cualquier subproceso, pero esta identidad de las colas de mensajes enruta el trabajo que se va a realizar en el subproceso del Looper .

Cuando estamos ejecutando algún código en otro subproceso y queremos enviar un Runnable para ser ejecutado en el subproceso de UI, podríamos hacerlo así:

 // h is a Handler that we constructed on the UI thread. public void run_on_ui_thread(final Handler h, final Runnable r) { // Associate a Message with our Handler and set the Message's // callback member to our Runnable: final Message message = Message.obtain(h, r); // The target is the Handler, so this asks our Handler to put // the Message in its message queue, which is the exact same // message queue associated with the Looper on the thread on // which the Handler was created: message.sendToTarget(); } 

Trato de implementar estas interfaces por mí mismo con el fin de comprender el concepto. Por simplicidad, sólo use la interfaz necesaria. Aquí está mi código de prueba:

 import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingQueue; public class TestLooper { public static void main(String[] args) { UIThread thread = new UIThread(); thread.start(); Handler mHandler = new Handler(thread.looper); new WorkThread(mHandler, "out thread").run(); } } class Looper { private BlockingQueue<Message> message_list = new LinkedBlockingQueue<Message>(); public void loop() { try { while (!Thread.interrupted()) { Message m = message_list.take(); m.exeute(); } } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } public void insertMessage(Message msg) { message_list.add(msg); } } class Message { String data; Handler handler; public Message(Handler handler) { this.handler = handler; } public void setData(String data) { this.data = data; } public void exeute() { handler.handleMessage(this); } } class Handler { Looper looper; public Handler(Looper looper) { this.looper = looper; } public void dispatchMessage(Message msg) { System.out.println("Handler dispatchMessage" + Thread.currentThread()); looper.insertMessage(msg); } public Message obtainMessage() { return new Message(this); } public void handleMessage(Message m) { System.out.println("handleMessage:" + m.data + Thread.currentThread()); } } class WorkThread extends Thread { Handler handler; String tag; public WorkThread(Handler handler, String tag) { this.handler = handler; this.tag = tag; } public void run() { System.out.println("WorkThread run" + Thread.currentThread()); Message m = handler.obtainMessage(); m.setData("message " + tag); handler.dispatchMessage(m); } } class UIThread extends Thread { public Looper looper = new Looper(); public void run() { //create handler in ui thread Handler mHandler = new Handler(looper); new WorkThread(mHandler, "inter thread").run(); System.out.println("thead run" + Thread.currentThread()); looper.loop(); } } 
  • ¿Cómo puedo mantener vivo un hilo de conexión? (¿Necesito usar un daemon?)
  • En android, ¿por qué la interfaz de usuario no se puede actualizar fuera del hilo de la interfaz de usuario?
  • Nuevo Runnable () pero no nuevo hilo?
  • ¿Por qué parece que tomar mucho tiempo para que este bloque sincronizado obtenga un bloqueo?
  • Runnable se publica correctamente pero no se ejecuta
  • Cómo actualizar la vista de imagen inmediatamente
  • ¿Cómo puedo reiniciar un hilo en java / Android desde un botón?
  • ¿Cómo funciona maximumPoolSize de ThreadPoolExecutor?
  • ¿Qué biblioteca de tareas prioritarias de Android (Asynk Task, Multithreading) recomendaría para Android?
  • Java.lang.RuntimeException: Sólo se puede crear un Looper por subproceso
  • Android - Async Tarea de comportamiento en 2.3.3 y 4.0 OS
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.