Explicación detallada del flujo del mecanismo del controlador de Android

Explicación detallada del flujo del mecanismo del controlador de Android

El mecanismo del controlador consta de las siguientes partes:

.Handler
.Message
.MessageQueue
.Looper

Introducción general del proceso:

1. Cuando se inicia el proceso,
se crea un Looper correspondiente a su propio hilo en el método principal para el hilo principal. Se crea una cola de mensajes MessageQueue y se mantiene cuando se crea el Looper. Al mismo tiempo, se llama a Looper.loop () en el método principal para realizar un bucle infinito para atravesar la cola de mensajes mantenida por Looper.

En segundo lugar, cree un controlador
Si se crea en el hilo principal, el controlador puede obtener el enlazador correspondiente al hilo principal y mantenerlo. Si está creando un controlador en un subproceso secundario, debe llamar al método de creación de looper y llamar a Looper.loop usted mismo. De lo contrario, se informará un error.

3. Cree y envíe
un mensaje: el mensaje creado encapsula su propia información de controlador en el mensaje y finalmente se envía a la cola de mensajes de MessageQueue.

Cuarto, reciba el mensaje
en Looper.loop, tome el mensaje de MessageQueue y distribuya el procesamiento de acuerdo con la información del controlador correspondiente

Correspondencia:
un hilo Thread corresponde a un Looper único, y la información correspondiente se almacena en ThreadLocal.
Un objeto Looper tiene su propia MessageQueue.
En el mismo hilo, no importa cuántos manejadores se creen, solo hay un Looper y un MessageQueue.
Por lo tanto, cuando se crea el controlador, se vincula al hilo correspondiente de acuerdo con Looper. Incluso si otro hilo envía un mensaje de acuerdo con el controlador, se enviará a la cola de mensajes de Looper correspondiente al controlador y se distribuirá.

Análisis de código:

A continuación, dé un ejemplo simple y analice brevemente el código original correspondiente al proceso principal:

private Handler handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
               
            }
        }
    };
    new Thread() {
            public void run() {
                    Message message = handler.obtainMessage();
                    message.what = 100;
                    message.obj = "测试+ : " + i;
                    handler.sendMessage(message);
            }
        }.start();

Análisis:
controlador: responsable de enviar mensajes y recibir mensajes devueltos, análisis del código fuente de la parte principal del proceso

public class Handler {
    //主要变量
    final Looper mLooper;//与每个线程对应的looper
    final MessageQueue mQueue;//消息队列
    final Callback mCallback;//是否同步

    //构造方法
    public Handler(Callback callback, boolean async) {
        if (FIND_POTENTIAL_LEAKS) {
            ..
        }
        mLooper = Looper.myLooper();//根据当前线程信息在ThreadLocal中获取线程对应的looper
        注:
        .如果是在子线程中创建,需要调用Looper.prepare( boolean quitAllowed)
        .该方法的作用是获取调用方法的线程信息,判断当前线程是否有looper,没有则创建。
        .在ActivityThread中创建过主线程的looper

        if (mLooper == null) {
            ..
        }
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }


//从消息对象池中取出消息
    public final Message obtainMessage()
    {
        return Message.obtain(this);
    }


//发送消息的各种方法
handler.sendMessage(message);
handler.sendMessageDelayed(
    Message msg, long delayMillis);
handler.sendMessageAtTime(
    Message msg, long uptimeMillis);
handler.sendEmptyMessage(
    int what);
handler.sendEmptyMessageDelayed(
    int what, long delayMillis);
handler.sendMessageAtFrontOfQueue(
    Message msg);
handler.sendEmptyMessageAtTime(
    int what, long uptimeMillis);

    //最终都是调用来发送
    private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        msg.target = this;//将当前handler信息封装进Message
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }


//分发消息

    /**
     * 该方法在Looper中next()方法中被调用  根据msg.target来区分
     */
    public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }

}

Eventos principales:

1. Al llamar: nuevo Handler ():
1. En Handler: Looper.myLooper ();
Obtenga el looper correspondiente al hilo actual en ThreadLocal. El looper correspondiente al hilo principal se ha creado en el método principal

2. mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
saque la cola de mensajes (mQueue) en el looper. Objeto de devolución de llamada. Ya sea para sincronizar. Encapsulado en el objeto controlador

En segundo lugar, al llamar: handler.obtainMessage (): obtenga
el mensaje en el grupo de objetos de mensaje.

3. Al llamar: sendMessage o varios métodos de envío:
finalmente llame a enqueueMessage, encapsule el objeto controlador actual en message.target
y llame a mQueue.enqueueMessage (msg, uptimeMillis) para enviar el mensaje a la cola de mensajes

Mensaje: responsable de encapsular el mensaje, el análisis del código fuente de la parte principal del proceso

public final class Message implements Parcelable {
    public int what;
    public int arg1;
    public int arg2;
    public Object obj;
    long when;
    Bundle data;
    Handler target;
    Runnable callback;
    Message next;
    public static final Object sPoolSync = new Object();
    private static Message sPool;
    private static int sPoolSize = 0;
    private static final int MAX_POOL_SIZE = 50;
    private static boolean gCheckRecycle = true;


    //被handler调用
    public static Message obtain(Handler h) {
        Message m = obtain();
        m.target = h;
        return m;
    }

    /**
     * 判断消息对象池中是否有消息,并从消息对象池中获取消息
     */
    public static Message obtain() {
        synchronized (sPoolSync) {
            if (sPool != null) {
                Message m = sPool;
                sPool = m.next;
                m.next = null;
                m.flags = 0; // clear in-use flag
                sPoolSize--;
                return m;
            }
        }
        return new Message();
    }

    省略其他好多方法...

Conclusión: Al usar el método get (this) en el controlador para obtener el objeto de mensaje en el grupo de mensajes, encapsule el actual en message.target

Looper: mantenga el bucle, obtenga mensajes de la cola de mensajes y distribuya los mensajes de acuerdo con el objetivo del mensaje, es decir, llame al método dispatchMessage del controlador correspondiente. Análisis del código fuente del proceso principal.

    public final class Looper {
    static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
    private static Looper sMainLooper;  // 主线程looper对象
    final MessageQueue mQueue; //消息队列
    final Thread mThread;//当前线程

    //构造方法:只有一个构造,并且创建mQueue 和给mThread 赋值
    private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
    }

    //初始化,如果handler在子线程中被调用,则要先调用此方法
    public static void prepare() {
        prepare(true);
    }

    //被上面方法调用
    private static void prepare(boolean quitAllowed) {
        if (sThreadLocal.get() != null) {
            //该方法内部根据当前线程信息判断是否创建了looper对象,如果有创建,则返回对应looper对象。
            //如果没有创建,则返回空
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        //创建一个looper对象,并将当前线程和该looper绑定
        sThreadLocal.set(new Looper(quitAllowed));
    }

    //主线程在创建的时候在ActivityThread类中main方法中被调用
     创建主线程对应的looper和给当前sMainLooper赋值
    public static void prepareMainLooper() {
        prepare(false);
        synchronized (Looper.class) {
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            sMainLooper = myLooper();
        }
    }

    //handler中获取looper的方法
    public static @Nullable
    Looper myLooper() {
        //该方法内部根据当前线程信息判断是否创建了looper对象,如果有创建,则返回对应looper对象。
        //如果没有创建,则返回空
        return sThreadLocal.get();
    }

    //开启loop循环查询队列,
    主线程对应的loop方法在main中被调用,如果在子线程中创建的handler,需要自己调用loop方法

    public static void loop() {
        final Looper me = myLooper();
        if (me == null) {
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }
        final MessageQueue queue = me.mQueue;

        省略众多代码....

        for (; ; ) {
        
        //队列的next()方法中处理基本逻辑,如果有消息:返回。如果没有消息:阻塞||等待
            Message msg = queue.next(); 
            if (msg == null) {
                return;
            }
            省略众多代码.....
            try {
                msg.target.dispatchMessage(msg);//根据targer(即handler)进行分发
                ..
            } finally {
                if (traceTag != 0) {
                    Trace.traceEnd(traceTag);
                }
            }
            //将消息放回消息池
            msg.recycleUnchecked();
        }
    }
}

Esta clase es responsable de hacer:

1. Cuando se llama a prepare ():
este método se puede definir como: crear un looper correspondiente al hilo de llamada, juzgar si el hilo llamado tiene un objeto looper, si hay un error, luego crear y vincular el hilo y el looper.

2. Cuando se llama a la construcción:
cree un MessageQueue y asigne un valor a la variable mThread (hilo actual)

3. Cuando se llama a myLooper ():
este método se puede definir como la obtención del bucle correspondiente al subproceso, juzgando si el subproceso llamado tiene un objeto de bucle y devolviendo si lo hay, sin devolver nulo.

4. El método prepareMainLooper () se llama en el método principal para crear el bucle correspondiente al hilo principal.

5. El método loop () se llama en main, que actúa como un bucle infinito para tomar mensajes de la cola de mensajes mantenida por el hilo actual correspondiente al looper y distribuirlos de acuerdo con el targer correspondiente al mensaje.

Nota: Cuando el código fuente se analiza aquí, hay una pregunta: por qué el bucle de Looper en el hilo principal no causa ANR
Explicación: ¿Por qué el bucle infinito Looper.loop no causa ANR?

MessageQueue: Cola de mensajes, reciba el mensaje enviado por el controlador y guárdelo en la cola, recorra la cola de mensajes en un bucle infinito y regrese al bucle. Cuando no haya ningún mensaje en la cola de mensajes, bloquee || espere. Análisis del código fuente del proceso principal.

public final class MessageQueue {

    private final boolean mQuitAllowed;
    是否允许退出
    private boolean mQuitting;//是否放弃消息队列

    @SuppressWarnings("unused")
    private long mPtr;

    private native static long nativeInit();//返回一个NativeMessageQueue,具体实现过程在Nativity层

    private native static void nativeWake(long ptr);//唤醒对应线程


    //构造
    MessageQueue(boolean quitAllowed) {
        mQuitAllowed = quitAllowed;//是否允许退出
        mPtr = nativeInit();//返回一个NativeMessageQueue,具体实现过程在Nativity层
    }


    //该方法就是在handler中调用send系列方法最终调用的方法,将消息放入消息队列
    boolean enqueueMessage(Message msg, long when) {
        if (msg.target == null) {//msg.target就是发送此消息的Handler
            throw new IllegalArgumentException("Message must have a target.");
        }
        if (msg.isInUse()) {//表示此消息正在被使用
            throw new IllegalStateException(msg + " This message is already in use.");
        }

        synchronized (this) {
            if (mQuitting) {//表示此消息队列被放弃
                IllegalStateException e = new IllegalStateException(
                        msg.target + " sending message to a Handler on a dead thread");
                Log.w(TAG, e.getMessage(), e);
                msg.recycle();
                return false;
            }

            msg.markInUse();
            msg.when = when;//将延迟时间封装到msg内部
            Message p = mMessages;//消息队列的第一个元素
            boolean needWake;//是否需要唤醒线程
            if (p == null || when == 0 || when < p.when) {
//如果此队列中头部元素是null(空的队列,一般是第一次),
或者此消息不是延时的消息,则此消息需要被立即处理,
此时会将这个消息作为新的头部元素,并将此消息的next指向旧的头部元素,
然后判断如果Looper获取消息的线程如果是阻塞状态则唤醒它,让它立刻去拿消息处理。

                msg.next = p;
                mMessages = msg;
                needWake = mBlocked;
            } else {
//如果此消息是延时的消息,则将其添加到队列中,
原理就是链表的添加新元素,按照when,也就是延迟的时间来插入的,
延迟的时间越长,越靠后,这样就得到一条有序的延时消息链表,
取出消息的时候,延迟时间越小的,就被先获取了。插入延时消息不需要唤醒Looper线程

                needWake = mBlocked && p.target == null && msg.isAsynchronous();
                Message prev;
                for (; ; ) {
                    prev = p;
                    p = p.next;
                    if (p == null || when < p.when) {
                        break;
                    }
                    if (needWake && p.isAsynchronous()) {
                        needWake = false;
                    }
                }
                msg.next = p; // invariant: p == prev.next
                prev.next = msg;
            }

            if (needWake) {
                nativeWake(mPtr);//唤醒线程
            }
        }
        return true;
    }


    //该方法就是在Looper中被调用的方法,返回一个message,如果队列为空,则阻塞
    Message next() {
        final long ptr = mPtr;
//从注释可以看出,只有looper被放弃的时候(调用了quit方法)才返回null,
mPtr是MessageQueue的一个long型成员变量,关联的是一个在C++层的MessageQueue,
阻塞操作就是通过底层的这个MessageQueue来操作的;当队列被放弃的时候其变为0
        if (ptr == 0) {
            return null;
        }

        int pendingIdleHandlerCount = -1;
        int nextPollTimeoutMillis = 0;
        for (; ; ) {
            if (nextPollTimeoutMillis != 0) {
                Binder.flushPendingCommands();
            }

//阻塞方法,主要是通过native层的epoll监听文件描述符的写入事件来实现的。
//如果nextPollTimeoutMillis=-1,一直阻塞不会超时。
//如果nextPollTimeoutMillis=0,不会阻塞,立即返回。
//如果nextPollTimeoutMillis>0,最长阻塞nextPollTimeoutMillis毫秒(超时),如果期间有程序唤醒会立即返回。
            nativePollOnce(ptr, nextPollTimeoutMillis);

            synchronized (this) {
                // Try to retrieve the next message.  Return if found.
                final long now = SystemClock.uptimeMillis();
                Message prevMsg = null;
                Message msg = mMessages;
                if (msg != null && msg.target == null) {
//msg.target == null表示此消息为消息屏障(通过postSyncBarrier方法发送来的)
//如果发现了一个消息屏障,会循环找出第一个异步消息(如果有异步消息的话),所有同步消息都将忽略(平常发送的一般都是同步消息)  
                    do {
                        prevMsg = msg;
                        msg = msg.next;
                    } while (msg != null && !msg.isAsynchronous());
                }
                if (msg != null) {
                    if (now < msg.when) {
// 如果消息此刻还没有到时间,设置一下阻塞时间nextPollTimeoutMillis,进入下次循环的时候会调用
//nativePollOnce(ptr, nextPollTimeoutMillis)进行阻塞;

                        nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                    } else {
                        //正常取出消息
                        //设置mBlocked = false代表目前没有阻塞
                        mBlocked = false;
                        if (prevMsg != null) {
                            prevMsg.next = msg.next;
                        } else {
                            mMessages = msg.next;
                        }
                        msg.next = null;
                        if (DEBUG) Log.v(TAG, "Returning message: " + msg);
                        msg.markInUse();
                        return msg;
                    }
                } else {
                    //没有消息,会一直阻塞,直到被唤醒
                    nextPollTimeoutMillis = -1;
                }

                // Process the quit message now that all pending messages have been handled.
                if (mQuitting) {
                    dispose();
                    return null;
                }
            }
            pendingIdleHandlerCount = 0;
            nextPollTimeoutMillis = 0;
        }
    }

}

Principales cosas que hacer:
construcción MessageQueue (): se crea cuando se crea Looper

enqueueMessage (): una serie de métodos de manejo del remitente eventualmente regresa a este método de la misma manera, coloca el mensaje en la cola de mensajes

next (): llamado en el método Looper.loop () para atravesar la cola y devolver el mensaje, bloqueando si no hay ningún mensaje en la cola

Publicado 60 artículos originales · 25 alabanzas · Más de 10,000 visitas

Supongo que te gusta

Origin blog.csdn.net/qq_41466437/article/details/104628280
Recomendado
Clasificación