Manejar mecanismo de mensaje

Manejar mecanismo de mensaje

descripción general

En primer lugar, hablemos de por qué diseñamos un mango.

Ahora una pregunta, si no hay un controlador, ¿cómo se sincronizan los datos entre dos subprocesos?
Lograr esto inevitablemente requerirá un mecanismo de bloqueo, pero solo el subproceso principal puede actualizar la interfaz de usuario. En este momento, el bloqueo y el bloqueo afectarán en gran medida al subproceso principal. Entonces, si la comunicación del subproceso es posible, ¿la transmisión de datos debe ser secuencial? ?La cola vendrá. Similar a un modelo de consumidor productor. La función más importante de Handler es la comunicación de subprocesos, no el cambio de subprocesos.

cognición básica

En primer lugar, tenemos una comprensión básica del manejo. Sabemos que el proceso de apertura de la aplicación de nuestro programa es un lanzamiento (aplicación) para producir un cigoto, y luego un cigoto para incubar un jvm independiente. Luego iniciamos nuestra aplicación en este jvm , .
AcitivityThreadenprincipalel primero que se llama cuando se inicia la aplicación es la , y el hilo principal Looper.quit() no está permitido , de lo contrario, la aplicación se cerrará .

Muchas aplicaciones son muchos procesos, por lo que existe una comunicación multiproceso IPC, puede usar Binder o socket, el socket puede comunicarse en dos terminales, o dos procesos pueden comunicarse en el mismo terminal, y el proceso se divide en hilo principal y sub- subproceso, un proceso necesita al menos un subproceso, y ese subproceso es el subproceso principal, también llamado subproceso de interfaz de usuario.

P: ¿Por qué solo el hilo de la interfaz de usuario puede actualizar la interfaz de usuario? ¿No sería mejor agregar un bloqueo de sincronización?

Respuesta: Los controles de la interfaz de usuario no son seguros para subprocesos. El acceso simultáneo de varios subprocesos puede hacer que el control de la interfaz de usuario esté en un estado impredecible, y la razón por la cual el bloqueo de sincronización no se puede agregar al acceso del control de la interfaz de usuario es que el bloqueo hará que el Control de interfaz de usuario complicado e ineficiente El bloqueo bloquea la ejecución de ciertos procesos. A veces, algunas solicitudes de red, entrada y salida de datos y tareas que consumen mucho tiempo serán demasiado lentas en el subproceso principal y causarán bloqueos, lo que resultará en un subproceso de interfaz de usuario lento y efectos negativos para los usuarios, por lo que las tareas que requieren mucho tiempo se abren en otro subproceso para hacer a ellos. En Android, la interfaz de usuario solo se puede actualizar en el subproceso principal, no en los subprocesos.
Entonces, para resolver este problema, Android proporciona algo así como un mecanismo de manejo de mensajes.
El mecanismo de manejo de mensajes no es solo para resolver el problema de actualizar la interfaz de usuario, es solo un escenario de uso. La función más importante de Handler es la comunicación de subprocesos, no el cambio de subprocesos.

ThreadLocal

En primer lugar, no digas nada, hablemos primero de ThreadLocal. Después de entenderlo, puedes entender el mecanismo de mensaje de handle.

ThreadLocal generalmente se denomina "variable local de subproceso", lo que significa que algunos datos se limitan a subprocesos y hay diferentes copias de datos en diferentes subprocesos. En pocas palabras, cada subproceso corresponde a un valor. Solo puede almacenar y obtener los datos del subproceso A en este subproceso A, pero no puede obtener los datos del subproceso B, que es un poco como un mapa hash.
El siguiente es un ejemplo para ilustrar, primero defina un objeto ThreadLocal, seleccione el tipo booleano, como se muestra a continuación

private ThreadLocal<Boolean> mThreadLocal = new ThreadLocal<>();

Luego establezca y acceda a su valor en el subproceso principal, subproceso secundario 1 y subproceso secundario 2 respectivamente

private void threadLocal() {
        mThreadLocal.set(true);
        Log.d(TAG, "[Thread#main]threadLocal=" + mThreadLocal.get());
        new Thread() {
            @Override
            public void run() {
                super.run();
                mThreadLocal.set(false);
                Log.d(TAG, "[Thread#1]threadLocal=" + mThreadLocal.get());
            }
        }.start();

        new Thread() {
            @Override
            public void run() {
                super.run();
                Log.d(TAG, "[Thread#2]threadLocal=" + mThreadLocal.get());
            }
        }.start();
    }

Aunque se accede a un mismo objeto ThreadLocal en diferentes hilos, los valores obtenidos a través de ThreadLocal son diferentes.
La razón por la que esto es así, hablemos de cómo funciona.

Antes de hablar de su principio, hablemos del conocimiento de preparación.

public class Thread implements Runnable {
  
    ThreadLocal.ThreadLocalMap threadLocals = null;

  static class Entry extends WeakReference<ThreadLocal<?>> {
            /** The value associated with this ThreadLocal. */
            Object value;

            Entry(ThreadLocal<?> k, Object v) {
                super(k);
                value = v;
            }
        }

  private Entry[] table;
  }

Es decir, cada hilo tiene un

ThreadLocal.ThreadLocalMap threadLocals, y threadLocals tiene una matriz table[] dentro .

ok preparación conocimiento ha terminado. Continúe hablando sobre cómo funciona ThreadLocal.
De hecho, cuando ThreadLocal llama al método set(), set() obtiene el subproceso actual y luego, de acuerdo con
ThreadLocal.ThreadLocalMap threadLocals del subproceso actual, coloca el objeto de referencia de ThreadLocal junto con los datos que se colocarán en el subproceso. En la matriz table[] de ThreadLocal.ThreadLocalMap threadLocals, la posición de índice de referencia más 1 es la posición de índice de valor, que es similar a
table[0]=referencia ThreadLocal, table[1]=valor
. hilo puede poner n La relación correspondiente entre una referencia ThreadLocal y n valores es la relación de mapeo de +1. El método gei() de ThreadLocal también es el mismo, primero
obtenga el subproceso actual y luego obtenga el valor correspondiente en la matriz table[] de acuerdo con la referencia ThreadLocal del subproceso actual en la matriz table[] de ThreadLocal.ThreadLocalMap threadLocals .

En resumen, es decir, cada subproceso tiene un ThreadLocal.ThreadLocalMap threadLocals, y este ThreadLocal.ThreadLocalMap threadLocals tiene una matriz table[] Cuando se establece una referencia de objeto ThreadLocal en un subproceso, la referencia de objeto actual y el valor de set se coloca en la matriz table[] del subproceso actual, y la posición de índice de la referencia más 1 es la posición de índice del valor, que es similar a table[0]=ThreadLocal reference, table[1]=value, luego, cuando se repite la referencia ThreadLocal Cuando se llama a get, el valor correspondiente se obtendrá de la matriz table[] del subproceso actual de acuerdo con esta referencia. De esta manera, se crean nuevos n ThreadLocals, se establecen y obtienen, y cada hilo puede poner n referencias ThreadLocal y n valores.
Por lo tanto, para el mismo objeto ThreadLocal, se establecen diferentes valores en diferentes subprocesos, y los valores obtenidos en diferentes subprocesos son diferentes.

Manejar mecanismo de mensaje

Primer vistazo a su uso básico

Hay básicamente dos tipos de operaciones de manejo.

El primero es usar handleMessage. Los pasos son para realizar tareas que requieren mucho tiempo en el hilo abierto. Cuando es necesario cambiar la interfaz de usuario, se envía un mensaje y luego se realiza el cambio en el controlador del hilo principal, que también es incluido en el hilo principal.
El código fuente similar se ve así:

android.os.Handler handler =new android.os.Handler(){//注意 Handler 包要用os的
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            //收到消息要做的更改UI的操作,msg是收到的消息
            }

        

    };//收到消息改变UI
       Button button = (Button) findViewById(R.id.c);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Thread thread = new Thread(new Runnable() {
                    @Override
                    public void run() {
                                    handler.sendEmptyMessage(0x123);//开启线程做耗时工作,需要改变UI就发送信息给主线程的handler去做
                    }
                });
                thread.start();

            }
        });

Aquí, el subproceso que crea el controlador y el subproceso que ejecuta handleMessage son el mismo subproceso, mHandler se crea en el subproceso principal, por lo que su método handleMessage también se ejecuta en el subproceso principal. mHandler.sendMessage(Message) puede estar en cualquier subproceso, y
hay muchas variantes de sendMessage, que pueden enviar mensajes vacíos (solo llevan qué parámetro), mensajes retrasados, mensajes de tiempo, etc. La forma de usarlo es muy sencilla.

El segundo usa handler.post

Handler handler =new Handler();
    button.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    handler.post(new Runnable() {
                        @Override
                        public void run() {
                            textView.setText(R.string.sample);
                        }
                    });
                }
            }).start();
        }
    });

El controlador todavía se crea en el hilo principal, y luego el parámetro del método de publicación es el objeto Runnable, por lo que Runnalbe se ejecutará en el hilo principal (no tiene nada que ver con el hilo creado por Runnable o el hilo llamado por el método mHandler.post ).

También hay una función integrada conveniente llamada runOnUiThread , que llama a showResponse(finalResult) en un subproceso; esta función, finalresult, toma los datos requeridos por el control del subproceso al subproceso principal, y luego en el subproceso principal ,

 public void showResponse(final String s) {
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                mTextView.setText(s);
            }
        });
    }

Bueno, hablemos sobre el mecanismo de mensajes de handle.

En primer lugar,
looper es un ama de llaves, responsable de procesar los mensajes, y messagequeue es una cola, una estructura de datos de una lista enlazada individualmente, que almacena mensajes.

La clase Mensaje lleva muchos parámetros,

público int qué;
público int arg1;
público int arg2;
objeto público obj;

Establezca los parámetros del Mensaje de acuerdo con las necesidades Mensaje.Lo que generalmente es necesario para distinguir diferentes Mensajes y realizar diferentes operaciones. También puede configurar los dos campos int arg1 y arg2 de Message. Por supuesto, además de estos datos simples, también se puede configurar para transportar datos complejos.El tipo de campo obj es tipo de objeto, que puede ser cualquier tipo de datos. También puede configurar los datos del tipo de paquete a través del método setData de Message y obtener los datos del paquete a través del método getData.

  Message = new Message();
        msg.what = 1;
        msg.arg1 = 111;  可以设置arg1、arg2、obj等参数,传递这些数据
        msg.arg2 = 222;
        String zjs = "zjs";
        msg.obj = zjs;
         //String a= (String) msg.obj;
        Bundle bundle= new Bundle();
        bundle.putString("name","zjs");
        msg.setData(bundle);
       // msg.getData().getBundle("name");
  		 //msg .target为信息的handler对象,即这条信息是谁发送的。

El identificador es responsable de enviar mensajes a la cola de mensajes. Hay dos tipos de identificadores de envío, uno es para publicar un objeto ejecutable y el otro es para enviar un mensaje a la cola de mensajes. De hecho, el identificador envía una publicación al objeto ejecutable
. objeto, pero en realidad llama al método de envío.Esto es solo un contenedor alrededor de ejecutable. Mira el código fuente a continuación:

 public final boolean post(Runnable r)
    {
       return  sendMessageDelayed(getPostMessage(r), 0);
    }
 public final boolean sendMessage(Message msg)
    {
        return sendMessageDelayed(msg, 0);
    }

Se puede ver que ambos métodos se implementan llamando al método sendMessageDelayed, por lo que podemos saber que su lógica subyacente es consistente.
Sin embargo, cuando la capa inferior del método de publicación llama a sendMessageDelayed, usa getPostMessage® para convertir el objeto Runnable en un mensaje. Hacemos clic en getPostMessage() para ver:

 private static Message getPostMessage(Runnable r) {
        Message m = Message.obtain();
        m.callback = r;
        return m;
    }

Se puede saber de lo anterior. Publicar un ejecutable es en realidad enviar un mensaje, de hecho, colocar el ejecutable en message.callback, es decir,
message.callback=runable
, y finalmente enviar un objeto de mensaje a la cola de mensajes
.

El significado de esto:
para que sea más fácil para los desarrolladores llamar de acuerdo con las diferentes necesidades. Cuando necesitamos transferir una gran cantidad de datos, podemos usar sendMessage para lograrlo, ya que al asignar valores a diferentes variables miembro de Message, se puede encapsular en un objeto rico en datos para la transmisión; cuando solo necesitamos realizar una acción, use Runnable directamente, simplemente implemente el contenido de la acción en el método de ejecución.

Bien, echemos un vistazo al proceso.

En primer lugar, un subproceso tiene solo un looper y una cola de mensajes. Un subproceso puede tener varios controladores . Cuando estos controladores son nuevos, ya están vinculados al looper, la cola de mensajes y el subproceso actual. Después de eso, no importa qué controlador esté en el otro Ya sea que el hilo publique o envíe (de hecho, sabemos lo mismo de acuerdo con lo anterior), eventualmente se pasará al hilo donde se crea el identificador. El proceso específico es el siguiente:
se crea handle1 en el subproceso a, y luego se envía el mensaje en el subproceso b u otros subprocesos, luego este mensaje adjuntará el handle1 actual al message.target, es decir,
message.target = handle1. Eso es. El looper obtiene el mensaje de la cola de mensajes en función del mensaje.objetivo del mensaje para saber a qué identificador enviarlo más tarde,

luego se llamará al mensaje en cola de la cola de mensajes en el subproceso A. enqueuemessage es un método para insertar un mensaje en la cola de mensajes , lo más importante es messagequeue Hay un método next(), next seguirá buscando mensajes en la cola, devolverá este mensaje al looper y luego eliminará este mensaje de la cola, o bloqueará y esperará si no hay ningún mensaje .

Tenga en cuenta que la estructura de datos de messagequeue es una cola de prioridad implementada por una sola lista enlazada. ¿
Por qué dice eso?
Sabemos que messagequeue es un montacargas cargado con Mesaage, y hay una variable en la clase Mesaage llamada Mesaage next. Es decir, los mensajes en la cola de mensajes apuntan al siguiente mensaje desde el siguiente de un mensaje,
mensaje ->siguiente->mensaje ->siguiente->mensaje
se denomina lista de enlace único, y la lista de enlace único es la siguiente solo va en una dirección El siguiente índice, si es una lista de doble enlace, es bidireccional.

Y cada mensaje tiene su tiempo de operación correspondiente, es decir, no importa si el handle envía un mensaje con retraso o no, el mensaje enviado tiene su tiempo correspondiente, por ejemplo, cuando el handle envía un mensaje por primera vez, el retraso tiempo Son 10 s para la cola de mensajes, el siguiente mensaje, el tiempo de demora es de 5 s, ingresa a la cola de mensajes, ella hará un recorrido de todos los mensajes en la cola de mensajes, colocará el que necesita ser operado temprano, y luego colocará el mensaje de 5 s en la cima.

El método next() de la cola de mensajes debe habilitarse mediante el método Loopr.looper(), es decir, Loopr es la fuente del bucle sin fin abierto para encontrar el mensaje. La única forma de salir de este bucle es looper.quit() , después de looper.quit() , el método next() de messagequeue devolverá un valor nulo, y luego los dos bucles infinitos finalizarán, y luego todo terminará, y el controlador de envío () devolverá fasle. Otra forma de salir de looper es looper.quitsafely(), looper.quit() es finalizar inmediatamente y looper.quitsafely() es esperar a que los mensajes en la cola terminen antes de finalizar. Por lo tanto, cuando implemente el desarrollo, debe usar looper.quit() o looper.quitsafely() cuando no necesite trabajar; de lo contrario, el bucle sin fin seguirá buscando mensajes. A continuación, después de que el looper reciba el mensaje, llamará al método message.target.dispatchMessage,
es decir, sabrá a qué identificador pertenece el mensaje de acuerdo con el mensaje.objetivo, y luego llamará al método dispatchMessage() para el identificador .Veamos Descarga el código fuente
dispatchMessage

public void dispatchMessage(Message msg) {
    
    
    if (msg.callback != null) {
    
    
        // 如果有Runnbale,则直接执行它的run方法
        handleCallback(msg);
    } else {
    
    
        //如果有实现自己的callback接口
        if (mCallback != null) {
    
    
            //执行callback的handleMessage方法
            if (mCallback.handleMessage(msg)) {
    
    
                return;
            }
        }
        //否则执行自身的handleMessage方法
        handleMessage(msg);
    }
}

private static void handleCallback(Message message) {
    
    
    message.callback.run();
}

De acuerdo con el código fuente, podemos obtener claramente que, primero, juzgue si el mensaje tiene una devolución de llamada, es decir, ejecutable, luego ejecútelo y luego finalícelo. De lo contrario, juzgue si el método para recrear el identificador utiliza el método de devolución de llamada, es decir:

  Handler.Callback callback= new Handler.Callback() {
            @Override
            public boolean handleMessage(Message msg) {
                //处理消息
                return false;
            }
        };
        Handler handler= new Handler(callback);
        handler.sendEmptyMessage(123);

Por lo general, creamos un identificador derivando una subclase de identificador y reescribiendo handleMessage, es decir

android.os.Handler handler1=new android.os.Handler(){
            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);

            }
        };
        handler1.sendEmptyMessage(123);

La diferencia entre los métodos de creación es que la secuencia es diferente, si el handleMessage del Callback devuelve falso, los siguientes se pueden ejecutar con normalidad. Pero cuando se devuelve true, el mensaje se intercepta y el handleMessage subsiguiente no se ejecutará.

Entonces dispatchMessage primero juzga si el mensaje tiene una devolución de llamada, es decir, ejecutable, luego se ejecuta y luego finaliza. De lo contrario, juzgue si el método para recrear el identificador usa el método de devolución de llamada y llame al handleMessage de la devolución de llamada si es así, o llame a handleMessage() si no lo es.

Bueno, el proceso específico ha terminado, echemos un vistazo a algunos otros conocimientos de la aplicación.

Sabemos que cuando se crea el controlador, formará un sistema de bucle de mensajes con el looper del subproceso actual, entonces, ¿cómo encuentra el controlador el looper del subproceso actual y por qué cada subproceso tiene solo un looper? utiliza el conocimiento de Threadlocal anterior, Threadlocal puede almacenar y proporcionar datos sin interferir entre sí en diferentes subprocesos, y luego en el identificador, los datos almacenados son looper, y el looper se almacena en ThreadLocal. El código fuente garantiza que solo puede haber un conjunto, es decir, solo hay una referencia ThreadLocal, por lo tanto, también hay solo un looper. A través de Threadlocal, puede obtener fácilmente el looper de cada hilo. Por supuesto, cada hilo no tiene looper y debe crearse por sí mismo. , a saber:

Looper.prepare();//
Looper.loop()//开启循环

En el subproceso principal, el looper y la cola de mensajes se han creado automáticamente de forma predeterminada, por lo que el identificador se crea en el subproceso principal y se vincula a ellos. Si el identificador se crea en el subproceso, es necesario configurar el looper del subproceso. subproceso principal o cree un subproceso Looper.prepare (); (utilice el subproceso que necesite) para manejar, no se informará ninguna excepción.

Pregunta: Dado que varios identificadores pueden insertar mensajes en una cola de mensajes, y los identificadores pueden estar en diferentes subprocesos, ¿cómo garantiza internamente la seguridad de los subprocesos?

Respuesta: El método queuemessage en la cola de mensajes utiliza
sincronizado (esto), es decir, se toma el bloqueo de objeto de la cola de mensajes, es decir,
un subproceso tiene solo una cola de mensajes, por lo que la operación de inserción la realizan tantos identificadores, cualquiera que obtenga la cola de mensajes. bloqueo de objetos primero, inserta la operación, lo que da como resultado un sincrónico seguro para subprocesos

Necesitamos saber que el mecanismo del mensaje de manejo no es el cambio de subprocesos. El llamado cambio de subprocesos significa que el código detrás del mensaje de publicación del subproceso A no se ejecuta, y se cambia al subproceso B para su ejecución. Después de que el subproceso B es ejecutado, vuelve al subproceso A. Sin embargo, handle no es así. Handle es comunicación de subprocesos, es decir, el código detrás del mensaje de publicación del subproceso A continúa ejecutándose y no cambia al subproceso B, pero la comunicación permite que el código de procesamiento de mensajes del subproceso B lo haga.

¿Cómo debemos crear un mensaje?

Puede usar new para crear un mensaje, pero es mejor usar
Message msg = Message.obtain(); para obtener un mensaje.

Suponga que cuando se procesa un cuadro de mensaje que ocupa un tamaño de 10 m, la ubicación de la memoria y el tamaño que ocupa no se reciclarán, pero el contenido del área del cuadro se vaciará y luego se colocará en el grupo de búfer para la comodidad de la próxima repetición. uso Cuando usamos el método Message.obtain() para obtener un mensaje, primero verificaremos si hay un tamaño de cuadro que quiero del grupo de búfer.Si no hay ningún mensaje en el grupo de búfer, crearemos un mensaje. El propósito de hacer esto es evitar el oom causado por la fluctuación de la memoria causada por la fragmentación de la memoria.

La razón de esto es que sabemos que cada vez que creamos un objeto con new, cortaremos un área en una posición aleatoria en el tamaño total de la memoria de la aplicación, y si continuamos descontando muchos agujeros grandes y pequeños en un período de tiempo corto, aunque habrá recuperación de GC, mientras se crea y recicla continuamente en un período de tiempo tan corto, habrá mucha fragmentación de la memoria, lo que causará inestabilidad en la memoria, aunque hay suficiente tamaño en la memoria, pero se deducen muchas posiciones, por lo que no hay una posición grande continua, por lo que es oom. Por lo tanto, usar Message.obtain(); primero verificará si ya existe un cuadro adecuado y luego irá al nuevo objeto.
inserte la descripción de la imagen aquí

No envíe el mismo Mensaje dos veces. Por ejemplo, el siguiente código es problemático:

//同一个Message发送了两次
Message msg = Message.obtain();
handler.sendMessage(msg);
handler.sendMessage(msg);

Esto se debe a que todos los mensajes se envían a MessageQueue para su almacenamiento y luego esperan su procesamiento. Si un objeto de mensaje está en MessageQueue, se informará un error cuando se almacene nuevamente en MessageQueue.

Pregunta: Sabemos por lo anterior que el subproceso principal tiene un looper, y el looper ha estado haciendo un ciclo sin fin, entonces, ¿por qué no hace que la aplicación se congele, es decir, anr.

No hay relación entre los dos.anr se produce cuando el mensaje no se procesa en un tiempo fijo, es decir, por ejemplo, looper tarda demasiado en ejecutar un mensaje en un bucle sin fin en el hilo principal y provoca otros eventos de mensaje. en la cola para no responder Anr es causado, y el bucle infinito looper.looper es que no hay ningún mensaje en la cola, bloquea el sueño y se despierta inmediatamente cuando hay un mensaje. Estos dos son dos cosas diferentes.


En la barrera de sincronización, sabemos que los mensajes del hilo se colocan todos en la misma MessageQueue, y se ordenan según el tiempo correspondiente de cada mensaje. El looper no necesita recorrer toda la información de MessageQueue. , solo necesita obtener

La configuración de una barrera de sincronización se realiza mediante

Método postSyncBarrier de MessageQueue.
Establecer un mensaje de forma asíncrona es llamar

Message message=Message.obtain();
message.setAsynchronous(true);
handler.sendMessage(message);

Luego finalmente deshace la barrera de sincronización, llamando

MessageQueue的removeSyncBarrier这个方法

Entonces, ¿cuál es su razón de ser?
Al principio, cuando configura el mensaje como un mensaje asíncrono y luego llama a postSyncBarrier de MessageQueue, en postSyncBarrier

/**
*
@hide
**/
public int postSyncBarrier() {
return postSyncBarrier(SystemClock.uptimeMillis());
}
private int postSyncBarrier(long when) {
// Enqueue a new sync barrier token
synchronized (this) {
final int token = mNextBarrierToken++;
//从消息池中获取Message
final Message msg = Message.obtain();
msg.markInUse();
//就是这里!!!初始化Message对象的时候,并没有给target赋值,因此 target==null
msg.when = when;
msg.arg1 = token;
Message prev = null;
Message p = mMessages;
if (when != 0) {
while (p != null && p.when <= when) {
//如果开启同步屏障的时间(假设记为T)T不为0,且当前的同步消息里有时间小于T,则prev也不为null
prev = p;
p = p.next;
}
}
//根据prev是不是为null,将 msg 按照时间顺序插入到 消息队列(链表)的合适位置
if (prev != null) { // invariant: p == prev.next
msg.next = p;
prev.next = msg;
} else {
msg.next = p;
mMessages = msg;
}
return token;
}
}

Se puede ver que, de esta manera, un mensaje target == null ingresa a la cola de mensajes. (Es decir, se establece una barrera de sincronización en la cola de mensajes).
Entonces. Decimos que looper llamará al next() de MessageQueue para devolver un mensaje, y el primero en esta cola de mensajes. Cuando next() de MessageQueue encuentra que el destino del primer mensaje en la cola == nulo, recorrerá toda la cola para encontrar mensajes asíncronos y luego excluirá el procesamiento síncrono.

Bueno, si lee todo el artículo detenidamente, espero que pueda ayudarlo a comprender mejor el mecanismo de manejo de mensajes.

Supongo que te gusta

Origin blog.csdn.net/weixin_43836998/article/details/129789108
Recomendado
Clasificación