Análisis de código fuente del mecanismo de mensaje de 4 controladores

    Handler es un mecanismo de devolución de llamada asíncrono proporcionado por Android. Realiza principalmente la comunicación entre hilos de mensajes. Entonces surge la pregunta. ¿Por qué necesitamos Handler? Hay dos razones principales. Una es evitar operaciones que consumen mucho tiempo bloqueando el hilo principal ( UI atascada o ANR, el sistema Android requiere que el usuario coloque estas operaciones en el subproceso, 2 Android también requiere que la IU no se pueda actualizar en el subproceso (se estima que se basa en problemas de seguridad del subproceso ), esto requiere un mecanismo, en el subproceso El mensaje se pasa al subproceso principal en el subproceso, por lo que hay un controlador.

    Para implementar la devolución de llamada asíncrona, no es suficiente tener solo el controlador. Android también proporciona Looper, Message y MessageQueue. Estas clases juntas realizan la función de devolución de llamada asíncrona. Por lo general, creamos una instancia de un objeto Handler mHandler directamente en la actividad, enviamos un mensaje a través de mHandler en el subproceso secundario y luego recibimos el método de procesamiento en el método de devolución de llamada que se pasa al crear una instancia del controlador. hilo. Por lo tanto, comenzamos con la creación de instancias de Handler para un análisis en profundidad.

1, comienza a crear instancias de Handler

1,在主线程实例化Handler
private Handler mHandler=new Handler(new Handler.Callback() {
        @Override
        public boolean handleMessage(@NonNull Message msg) {
            return false;
        }
    });

2,进入Handler内部实现,到了最终的构造方法中

   public Handler(@Nullable Callback callback, boolean async) {
        ...
        mLooper = Looper.myLooper();//取出保存好的Looper
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread " + Thread.currentThread()
                        + " that has not called Looper.prepare()");
        }
        mQueue = mLooper.mQueue;//取出Looper中的消息队列
        mCallback = callback;   //后面处理消息的回调
        mAsynchronous = async;
    }

3,取出保存好的Looper的过程
   public static @Nullable Looper myLooper() {
        return sThreadLocal.get();
    }

4,ThreadLocal是什么,起的作用是什么?具体的看下ThreadLocal中set/get()的实现
ThreadLocal.java

  ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
    }

    public void set(T value) {
        Thread t = Thread.currentThread();//当前线程
        ThreadLocalMap map = getMap(t);//得到线程中的一个变量,作用类似于HashMap,可以通过k-v保存数据
        if (map != null)
            map.set(this, value);       //map以ThreadLocal为key,以Looper为value
        else
            createMap(t, value);        //创建一个以ThreadLocal为key,以Looper为value的map给thread
    }

   public T get() {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null) {
            ThreadLocalMap.Entry e = map.getEntry(this);//取出map中的value,也就是looper
            if (e != null) {
                @SuppressWarnings("unchecked")
                T result = (T)e.value;
                return result;
            }
        }
        return setInitialValue();
    }

所以,Looper中的sThreadLocal明着像是一个保存looper的容器,但实际却是自己为key,looper为value,保存在线程Thread中的map中

2. Luego de instanciado el Handler, vemos que internamente se sacan el Looper y MessageQueue, por lo que es necesario saber cómo se generan estos

1,在主线程中取出来的,那肯定是在主线程实例的,而且是在主线程(ActivityThread)刚启动的时候

ActivityThread.java
   public static void main(String[] args) {
        ...
        Looper.prepareMainLooper();//重点1
        ...
        ActivityThread thread = new ActivityThread();
        thread.attach(false, startSeq);//这里之前详细分析过了,主要为了application/activity的创建和启动
        ...
        Looper.loop();//重点2

        throw new RuntimeException("Main thread loop unexpectedly exited");//抛异常, 主线程的loop出乎意料的退出了
    }

2,重点1,Looper是如何产生的?
    Looper.java
   public static void prepareMainLooper() {
        prepare(false);//关键语句,实例一个Looper,并保存
        synchronized (Looper.class) {
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            sMainLooper = myLooper();//实例化的Looper赋给sMainLooper
        }
    }   

  private static void prepare(boolean quitAllowed) {
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        sThreadLocal.set(new Looper(quitAllowed));//通过threadLocad保存new出来的Looper
    }

 public static @Nullable Looper myLooper() {
        return sThreadLocal.get();//取出来
    }

3,重点2 Looper是如何工作的?
     public static void loop() {
        final Looper me = myLooper();
        ...        
        final MessageQueue queue = me.mQueue;
        ...
        for (;;) {
            Message msg = queue.next(); // might block 官方注解:可能阻塞,后续分析
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }
            msg.target.dispatchMessage(msg);
        ...
        }
    }


简化以后就很明了了,loop()中取出之前实例好的looper,并取出looper中的消息队列queue,开启死循环,不停的执行消息队列取消息操作,
message.target是私有变量handler

Handler.java

    public void dispatchMessage(Message msg) {
        if (msg.callback != null) {//一般常见于使用handler.postXX(Runnable)发送的消息
            handleCallback(msg);
        } else {
            if (mCallback != null) {这是之前实例化Handler传进去的回调监听类
                if (mCallback.handleMessage(msg)) {//回调类的处理方法,实际在activity上,这样就把消息队列的消息传到主线程上了
                    return;
                }
            }
            handleMessage(msg);//如果实例化Handler未传入回调类,则必须重新Handler本身的HandleMessage()方法,就是这个方法,同样的,把消息传到主线程上了
        }
    }

小结:通过查看Looper的实例化和Looper.loop()的调用,我们清楚的看到了消息的处理流程,
loop()依次从messageQueue中取出message,然后从message中取出handler去分配处理消息。

3. Ahora que el mensaje se ha sacado de la cola de mensajes, debe haber un proceso para colocar el mensaje en la cola de mensajes.

1,通过handler发送message的方法有2类,一种是send->Message系列,一种是post->Runnable系统

方法中带Message参数    
sendMessage()//常用
sendMessageDelayed()//延迟接收的消息
sendMessageAtTime()//准确时间接收的消息
sendMessageAtFrontQueue()//见名知义,放到消息队列最前面,也就是马上执行的消息
sendEmptyMessage()//空消息
sendEmptyMessageAtTime()
sendEmptyMessageDelayed()

方法中带Runnable参数,通过getPostMessage()生成Message,runnable参数传入到message中(处理消息时使用此回调)
post()
postDelayed()
postAtTime()
postAtFrontOfQueue()

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

上面所有发送消息的方法,最终都是通过sendMessageAtTime(Message msg, long uptimeMillis)来发送的(方法千千万,最终汇总),uptimeMillis为处理消息的时间    


 public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
        MessageQueue queue = mQueue;//这是Looper中的消息队列
        if (queue == null) {
            RuntimeException e = new RuntimeException(
                    this + " sendMessageAtTime() called with no mQueue");
            Log.w("Looper", e.getMessage(), e);
            return false;
        }
        return enqueueMessage(queue, msg, uptimeMillis);
    }

   private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        msg.target = this;//this就是当前的handler,处理消息时就是用的msg中的target
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);//最终,handler把消息送到了消息队列中,然后消息队列进行入队操作
    }

4. Toque los tambores y pase las flores, el mensaje finalmente se enviará a la cola de mensajes

0,在消息message进入消息队列messagequeue之前,我们需要对Message和MessageQueue的基本结构有初步的了解,方便了解入队的整个过程
Message:有个when字段,表示当前消息处理的时间,同样的,message关联指向了另一个message,字段名为next,所以,整个Message可以组成一个链表结构
MessageQueue:消息队列管理保存着消息,因为Message可以组成一个链表结构,所以MessageQueue只需要持有一个Message对象就能完成对Message的保存和管理,
因为消息队列有处理时间的要求,所以消息队列持有的头消息必定是最早处理的(when最小),所以在入队的过程就必然需要有对时间的比较。

1,enqueue的就是入队的意思
boolean enqueueMessage(Message msg, long when) {
        ...

        synchronized (this) {
           ...

            msg.markInUse();
            msg.when = when;//将消息处理的时间放到消息的when字段中
            Message p = mMessages;//队列持有的头消息
            boolean needWake;
            if (p == null || when == 0 || when < p.when) {//如果没有头消息 或 当前消息要立即处理 或当前消息处理时间早于头消息时间
                // New head, wake up the event queue if blocked.
                msg.next = p;                            //头消息变成msg的next消息,这样就当前消息变成了头消息,原先的头消息变成第二个消息对象
                mMessages = msg;                        //msg赋值给消息对象持有的头消息
                needWake = mBlocked;                    //不需要唤醒,这个变量后面再讨论
            } else {                                    //下面的操作是msg要插入到消息链表中间去

                needWake = mBlocked && p.target == null && msg.isAsynchronous();
                Message prev;//临时变量
                for (;;) {//死循环遍历消息链表,查找msg需要插入的位置
                    prev = p;//当前查找的消息放到prev上,第一次当然是消息队列持有的头消息
                    p = p.next;//取出下一个消息为p   此时 prev->p
                    if (p == null || when < p.when) {//如果下一个消息为空(到链表尾巴上了) 或者时间戳小于下一个消息的时间戳 则位置找到了
                        break;//跳出死循环,位置找到了,msg要插在p前面,此时prev->p,执行插入后期望的顺序为 prev->msg->p
                    }
                    if (needWake && p.isAsynchronous()) {
                        needWake = false;
                    }
                }
                msg.next = p;   //
                prev.next = msg;//这2步实现的功能就是链表顺序变为 prev->msg->p 此时头消息还是mMessage,但msg已经到它的链表上了
            }

            // We can assume mPtr != 0 because mQuitting is false.
            if (needWake) {
                nativeWake(mPtr);       //有消息来了,底层唤醒Looper
            }
        }
        return true;
    }

2,这样就很明了了,发送的消息经过比较和插入,终于进入到消息链表中了,结合前面的Looper.loop(),这样,整个流程就清楚了。

Resumen: el proceso general es así: en la etapa inicial de la creación del subproceso principal (ActivithThread.man()), mientras se crea Looper, se crea una cola de mensajes MessageQueue en el método de construcción de Looper, y Looper se llama después de main( loop(), loop() llamará a Messagequeue instanciado en Looper, obtendrá continuamente el mensaje a través de queue.next(), y luego programará y procesará el mensaje a través del objetivo (es decir, el controlador) establecido por el mensaje , y también podemos usar la instancia Una serie de métodos de envío/publicación del Controlador optimizado para enviar mensajes a la cola de mensajes. Toma una castaña en realidad:

Siempre que la estación de metro funcione normalmente, las máquinas que hayan pasado la inspección de seguridad deben estar encendidas y en funcionamiento. La estación de metro se puede comparar con ActivityThread, la máquina de inspección de seguridad es Looper y el equipo de inspección de seguridad funciona continuamente ( llamando a Looper.loop()).El equipo de inspección de seguridad tiene una correa de transmisión (MessgaQueue), cada equipaje (Mensaje) debe ser colocado en la correa transportadora, el proceso de colocación es el send/post() del manejador, luego el equipaje se prueba y no hay problema, se puede quitar del otro extremo (handler.dispatchMessage(message)), si la operación handler.send/post() se ejecuta en otro hilo, cuando se recupera el mensaje y programado para su procesamiento, ya está en el subproceso principal, que realiza una devolución de llamada asincrónica.

 

El principio de funcionamiento de Handler es aproximadamente como se muestra en la figura

De esta forma, se soluciona todo el flujo de trabajo de Handler.

Por supuesto, cuando se trata de bucles infinitos, ¿por qué looper.loop() no provoca el bloqueo de subprocesos debido a los bucles infinitos?

答案很简单,整个App因为主线程中loop.looper()的死循环而保持活动,如果跳出死循环,整个App会抛异常,
ActivityThread.main(){
...
    Looper.loop();//重点2
   throw new RuntimeException("Main thread loop unexpectedly exited");//抛异常, 主线程的loop出乎意料的退出了 这句话正常情况下不会被执行
}
而且,造成主线程堵塞是因为对Message处理的超时造成的,以之前举例说明,安检设备因为传输带不断循环而保持正常工作,一旦行李过重,用户从传送带上搬离行李过慢才会造成后续行李的卡住(各类事件不被处理,造成阻塞)。
同样的,在消息队列中没有消息时,主线程会进入休眠状态,即消息队列执行next()方式时,会根据下一个消息还有多久执行系统底层方法nativePollOnce()释放资源进入休眠状态
        MessageQueue.next(){
             for (;;) {
                if (nextPollTimeoutMillis != 0) {
                Binder.flushPendingCommands();
            }

            nativePollOnce(ptr, nextPollTimeoutMillis);//直到下条消息到达或者有事务发生,
        }

而一旦有最新消息事件进入到消息队列时,一旦新事件需要马上执行或者到了执行时间的时间,会通过系统底层方法nativeWake()唤醒主线程进行工作
        MessageQueue.enqueueMessage(){

        ...
          if (needWake) {
                nativeWake(mPtr);//通过往 pipe 管道写端写入数据来唤醒主线程工作,这里采用的 epoll 机制
            }
        }

El mecanismo de suspensión que ejecuta el hilo principal cuando no hay tareas optimiza en gran medida el rendimiento, del mismo modo, a la hora de enviar mensajes, utilizar get es más eficiente que las instancias directas.

1,Message.java

  /**
     * Return a new Message instance from the global pool. Allows us to
     * avoid allocating new objects in many cases.
     *从全局池中返回一个新消息实例
     */
 public static Message obtain() {
        synchronized (sPoolSync) {
            if (sPool != null) {
                Message m = sPool;//Message类内部也有一个消息头,相当于也有一个消息链表 头消息置于m
                sPool = m.next;     //下一个消息置为表头 
                m.next = null;
                m.flags = 0; // clear in-use flag
                sPoolSize--;//
                return m;//返回当初的表头消息
            }
        }
        return new Message();
    }

这样的话也不用每次都实例消息,最大限度的提高效率。
Resumen: como mecanismo de devolución de llamada de mensajes, Handler involucra relativamente pocas clases asociadas, y la estructura general es relativamente clara, pero la lógica interna aún es muy valiosa para aprender.

Supongo que te gusta

Origin blog.csdn.net/sunguanyong/article/details/127861061
Recomendado
Clasificación