¿Cuál es la relación entre Looper, Handler y MessageQueue en Android?

MessageQueue documentación / guía oficial de Android para Looper , Handler y MessageQueue . Pero no pude conseguirlo. Soy nuevo en Android, y me confundí mucho con estos conceptos.

Un Looper es un bucle de manejo de mensajes: lee y procesa los elementos de un MessageQueue . La clase Looper se usa generalmente junto con HandlerThread (una subclase de Thread ).

Un Handler es una clase de utilidad que facilita la interacción con un Looper principalmente publicando mensajes y objetos Runnable en el MessageQueue la MessageQueue . Cuando se crea un Handler , está vinculado a un Looper específico (y el hilo asociado y la cola de mensajes).

En el uso típico, usted crea e inicia un HandlerThread , luego crea un objeto Handler (u objetos) por el cual otros hilos pueden interactuar con la instancia HandlerThread . El Handler debe crearse mientras se ejecuta en HandlerThread , aunque una vez creado no hay restricciones sobre qué subprocesos pueden usar los métodos de progtwigción del post(Runnable) ( post(Runnable) , etc.)

El hilo principal (también conocido como subproceso de interfaz de usuario) en una aplicación de Android se configura como un hilo de controlador antes de que se cree su instancia de aplicación.

Aparte de los documentos de la clase, hay una buena discusión de todo esto aquí .

Es ampliamente conocido que es ilegal actualizar los componentes de la interfaz de usuario directamente desde hilos que no sean el hilo principal en Android. Este documento de Android ( Manejo de operaciones costosas en el subproceso de la interfaz de usuario ) sugiere los pasos a seguir si tenemos que iniciar un hilo por separado para hacer un trabajo costoso y actualizar la interfaz de usuario después de que se haya realizado. La idea es crear un objeto Handler asociado con el hilo principal , y publicar un Runnable en el momento apropiado. Este Runnable se invocará en el hilo principal . Este mecanismo se implementa con las clases Looper y Handler .

La clase Looper mantiene un MessageQueue , que contiene una lista de mensajes . Un personaje importante de Looper es que está asociado con el hilo dentro del cual se crea el Looper . Esta asociación se mantiene para siempre y no se puede romper ni cambiar. También tenga en cuenta que un hilo no se puede asociar con más de un Looper . Para garantizar esta asociación, Looper se almacena en almacenamiento local de subprocesos y no se puede crear a través de su constructor directamente. La única forma de crearlo es llamar a preparar el método estático en Looper . El método de preparación primero examina ThreadLocal del hilo actual para asegurarse de que no haya un Looper asociado al hilo. Después del examen, se crea un nuevo Looper y se guarda en ThreadLocal . Después de haber preparado el Looper , podemos llamar al método de bucle para verificar si hay mensajes nuevos y tener Handler para tratar con ellos.

Como su nombre lo indica, la clase Handler es principalmente responsable de manejar (agregar, eliminar, despachar) los mensajes del MessageQueue la MessageQueue actual. Una instancia de Handler también está vinculada a un hilo. La unión entre Handler y Thread se logra a través de Looper y MessageQueue . Un Handler siempre está ligado a un Looper , y posteriormente vinculado al hilo asociado con el Looper . A diferencia de Looper , múltiples instancias de Handler pueden vincularse al mismo hilo. Cada vez que llamamos post o cualquier método por igual en el Handler , se agrega un nuevo mensaje al MessageQueue asociado. El campo de destino del mensaje se establece en la instancia actual del Handler . Cuando el Looper recibió este mensaje, invoca dispatchMessage en el campo de destino del mensaje, de modo que el mensaje vuelva a la instancia de Handler que se manejará, pero en el hilo correcto. Las relaciones entre Looper , Handler y MessageQueue se muestran a continuación:

enter image description here

Comencemos con el Looper. Puede entender la relación entre Looper, Handler y MessageQueue más fácilmente cuando comprende lo que es Looper. También puede comprender mejor qué es Looper en el contexto del marco de GUI. Looper está hecho para hacer 2 cosas.

1) Looper transforma un hilo normal , que termina cuando su método run() retorna, en algo que se ejecuta continuamente hasta que se ejecuta la aplicación Android , que es necesaria en el marco GUI (Técnicamente, aún termina cuando el método run() retorna. aclaro lo que quiero decir, abajo).

2) Looper proporciona una cola donde los trabajos que se realizarán se ponen en cola , lo cual también es necesario en el marco de GUI.

Como sabrá, cuando se lanza una aplicación, el sistema crea un hilo de ejecución para la aplicación, llamado “principal”, y las aplicaciones de Android normalmente se ejecutan en un solo hilo por defecto el “hilo principal”. Pero el hilo principal no es un hilo secreto, especial . Es solo un hilo normal que también puedes crear con el new Thread() código new Thread() , lo que significa que termina cuando devuelve su método run() . Piensa en el siguiente ejemplo.

 public class HelloRunnable implements Runnable { public void run() { System.out.println("Hello from a thread!"); } public static void main(String args[]) { (new Thread(new HelloRunnable())).start(); } } 

Ahora, apliquemos este principio simple a la aplicación de Android. ¿Qué pasaría si una aplicación de Android se ejecuta en un hilo normal? Un hilo llamado “principal” o “UI” o lo que sea, inicia la aplicación y dibuja toda la IU. Entonces, la primera pantalla se muestra a los usuarios. ¿Y ahora qué? El hilo principal termina? No, no debería. Debería esperar hasta que los usuarios hagan algo, ¿verdad? Pero, ¿cómo podemos lograr este comportamiento? Bueno, podemos probar con Object.wait() o Thread.sleep() . Por ejemplo, el hilo principal finaliza su trabajo inicial para mostrar la primera pantalla y duerme. Se despierta, lo que significa que se interrumpe, cuando se busca un nuevo trabajo. Hasta ahora todo va bien, pero en este momento necesitamos una estructura de datos similar a una cola para tener múltiples trabajos. Piense en un caso cuando un usuario toca la pantalla en serie y una tarea tarda más tiempo en terminar. Entonces, necesitamos tener una estructura de datos para que los trabajos se realicen en la forma de los primeros en entrar, primero en salir. Además, se puede imaginar, la implementación de la ejecución ininterrumpida y el proceso de trabajo cuando se utiliza la interrupción no es fácil, y conduce a códigos complejos y, a menudo, inmanejables. Preferimos crear un nuevo mecanismo para tal fin, y de eso se trata Looper . El documento oficial de la clase Looper dice: “Los hilos por defecto no tienen un bucle de mensaje asociado a ellos”, y Looper es una clase “utilizada para ejecutar un bucle de mensaje para un hilo”. Ahora puedes entender lo que significa.

Pasemos a Handler y MessageQueue. Primero, MessageQueue es la cola que mencioné anteriormente. Reside dentro de un Looper, y eso es todo. Puede verificarlo con el código fuente de la clase Looper . La clase Looper tiene una variable miembro de MessageQueue.

Entonces, ¿qué es Handler? Si hay una cola, debe haber un método que nos permita poner una nueva tarea en cola, ¿no? Eso es lo que Handler hace. Podemos poner una nueva tarea en una cola (MessageQueue) usando varios métodos de post(Runnable r) . Eso es. Esto es todo sobre Looper, Handler y MessageQueue.

Mi última palabra es que, básicamente, Looper es una clase creada para solucionar un problema que ocurre en el marco de GUI. Pero este tipo de necesidades también pueden ocurrir en otras situaciones también. En realidad, es un patrón bastante famoso para la aplicación de varios hilos, y puede aprender más sobre esto en “Progtwigción concurrente en Java” por Doug Lea (Especialmente, el capítulo 4.1.4 “Trabajadores de hilos” sería útil). Además, puede imaginar que este tipo de mecanismo no es único en el marco de Android, pero todos los marcos de GUI pueden necesitar algo similar a esto. Puede encontrar casi el mismo mecanismo en el marco Java Swing.

MessageQueue : es una clase de bajo nivel que contiene la lista de mensajes que enviará un Looper . Los mensajes no se agregan directamente a un MessageQueue , sino a través de los objetos Handler asociados con el Looper . [ 3 ]

Looper : MessageQueue por un MessageQueue que contiene los mensajes que se MessageQueue . La tarea real de administrar la cola la realiza el Handler que es responsable de manejar (agregar, eliminar, despachar) los mensajes en la cola de mensajes. [ 2 ]

Handler : le permite enviar y procesar Message y Runnable objects asociados con MessageQueue un thread. Cada instancia de Handler está asociada con un único hilo y la cola de mensajes de ese hilo. [ 4 ]

Cuando crea un nuevo Handler , está vinculado a la fila / cola de mensajes del subproceso que lo está creando. A partir de ese momento, entregará los mensajes y los ejecutables a esa cola de mensajes y los ejecutará a medida que salgan del mensaje. cola

Amablemente, ve a través de la imagen de abajo [ 2 ] para una mejor comprensión.

enter image description here

MessageQueue, Handler, Looper en Android MessageQueue :

MessageQueue es un bucle de mensajes o una cola de mensajes que básicamente contiene una lista de mensajes o ejecutables (conjunto de códigos ejecutables).

En otras palabras, MessageQueue es una cola que tiene tareas llamadas mensajes que deben procesarse.

Nota: Android mantiene un MessageQueue en el hilo principal.

Looper :

Looper es un trabajador que sirve un MessageQueue para el hilo actual. Looper recorre la cola de mensajes y envía mensajes a los manejadores correspondientes para su procesamiento.

Cualquier hilo puede tener un único Looper único, esta restricción se logra mediante el uso de un concepto de almacenamiento ThreadLocal.

Beneficios de Looper y MessageQueue

Hay algunas ventajas para usar Looper y MessageQueue como se describe a continuación-

· Con el uso de MessageQueue, la ejecución es secuencial, por lo que en caso de subprocesos concurrentes esto evitará las condiciones de carrera.

· Normalmente, el hilo no se puede reutilizar una vez que se completa su trabajo. Pero el hilo con Looper se mantiene activo hasta que se llame al método de abandono para que no tenga que crear una nueva instancia cada vez que desee ejecutar un trabajo en segundo plano.

Entrenador de animales:

Un controlador permite comunicarse con el hilo de la interfaz de usuario desde otra cadena de fondo. Esto es útil en Android ya que Android no permite que otros hilos se comuniquen directamente con el hilo de la interfaz de usuario.

Un controlador le permite enviar y procesar mensajes y objetos ejecutables asociados con MessageQueue de un hilo. Cada instancia de Handler está asociada con un único hilo y la cola de mensajes de ese hilo.

Cuando crea un nuevo Manejador, está vinculado a la fila / cola de mensajes del hilo que lo está creando. A partir de ese punto, entregará los mensajes y los ejecutables a esa cola de mensajes y los ejecutará a medida que salgan de la cola de mensajes. .

Hay dos usos principales para un Manejador:

(1) Para progtwigr mensajes y ejecutables que se ejecutarán como algún punto en el futuro. En otras palabras, realice acciones en el mismo hilo en el futuro.

(2) Para poner en cola una acción que se realizará en un hilo diferente al suyo. En otras palabras, encola una acción para realizar en un hilo diferente.