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? 🙂
- ¿Hay una manera de pasar parámetros a un Runnable?
- Android BluetoothSocket - Tiempo de espera
- Acceso a vistas desde otro hilo (Android)
- Espere a que los hilos se completen antes de continuar
- Cuando ejecuto el subproceso segundo tiempo: java.lang.IllegalThreadStateException: El hilo ya comenzó
- Enhebrado en Android
- Cómo esperar a que Android runOnUiThread para ser terminado?
- Precisión del sueño del hilo
- Cómo suspender y reanudar los hilos en android?
- Hacer un hilo que cancela una llamada de InputStream.read () si ha pasado el tiempo 'x'
- ¿Se asignan automáticamente nuevos subprocesos a un núcleo de CPU diferente en Java?
- Manera eficiente de manipular los subprocesos RxJava
- Java.util.concurrent.RejectedExecutionException en Async Tarea Android
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 apublic 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 reemplazarhandleMessage()
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(); } }
- Android canvas drawText establecer el tamaño de la fuente de la anchura?
- Múltiples notificaciones para la misma actividad