Android 9.0 source code_mechanism--a comprehensive analysis of the Handler mechanism (principle)

#opening

#Core source code
123PY`(GH()4}Z9MTYA4M26.png
#Brief introduction

In the whole source code world of Android, there are two sharp swords, one is the Binder mechanism and the other is the Handler message mechanism. The message mechanism involves **MessageQueue/Message/Looper/Handler** these four classes.

Handler is a mechanism introduced in Android that allows developers to participate in message loops in processing threads. When we use Handler, we deal with Message the most. Message is a related class exposed to developers by the Handler mechanism. Most of the functions of Handler can be completed through the Message class.

As a programmer, we not only need to know how to use Handler, but also how it is implemented internally, which is the purpose of this article.
#Model

#####Message mechanism (Handler) mainly includes:

    ✨ Message:消息分为硬件产生的消息(如按钮、触摸)和软件生成的消息;
    ✨ MessageQueue:消息队列的主要功能向消息池投递消息(MessageQueue.enqueueMessage)和取走消息池的消息(MessageQueue.next);
    ✨ Handler:消息辅助类,主要功能向消息池发送各种消息事件(Handler.sendMessage)和处理相应消息事件(Handler.handleMessage);
    ✨ Looper:不断循环执行(Looper.loop),按分发机制将消息分发给目标处理者。

#Instance

 * A typical example of the implementation of a Looper thread,
 * using the separation of {@link #prepare} and {@link #loop} to create an
 * initial Handler to communicate with the Looper.
 */
class LooperThread extends Thread {
    public Handler mHandler;

    public void run() {
        Looper.prepare();

        mHandler = new Handler() {
            public void handleMessage(Message msg) {
                // process incoming messages here
            }
        };

        Looper.loop();
    }
}

###### Next we will explain around this example!

#onelooper

Message queue MessageQueue is just a place to store Messages. It is Looper that really makes the message queue circulate. Let's focus on analyzing Looper first.

Looper is used to loop the messages in the thread. By default, when we create a new thread, there is no message queue MessageQueue in this thread. In order to enable threads to bind to a message queue, we need the help of Looper: first we need to call Looper's prepare() method, and then call Looper's Loop() method.

It should be noted that both Looper.prepare() and Looper.loop() are called within the run method of the new thread, both of which are static methods.

public static void prepare() {...}
    private static void prepare(boolean quitAllowed) {...}
    public static void loop() {...}

#1.prepare()

Let's take a look at Looper.prepare(), the method is to make the Looper ready, and the Looper.loop() method can only be called after the Looper is ready.

public static void prepare() {    
        prepare(true);     // 无参,调用 prepare(boolean quitAllowed)
    }
private static void prepare(boolean quitAllowed) {
        if (sThreadLocal.get() != null) {
        // 每个线程只允许执行一次该方法,第二次执行时已有 Looper,则会抛出异常!
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        // 创建 Looper 对象,并保存到当前线程的本地存储区
        sThreadLocal.set(new Looper(quitAllowed));      
    }

The above code first obtains the Looper object bound to the thread sThreadLocal through sThreadLocal.get(). Since sThreadLocal is not bound to the Looper in the initial case, when the prepare method is called for the first time, sThreadLocal.get() returns null, not will throw an exception.

####2.ThreadLocal
ThreadLocal: Thread Local Storage (TLS for short), each thread has its own private local storage area, and different threads cannot access each other's TLS area.

TLS commonly used operation methods:

####3.set()

public void set(T value) {
        Thread t = Thread.currentThread();    // 获取当前线程 
        ThreadLocalMap map = getMap(t);       //查找当前线程的本地储存区
        if (map != null)
            map.set(this, value);             // 保存数据 value 到当前线程 this
        else
            createMap(t, value);
    }

###### Let's look at the getMap() function:

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

###### Determine if the map is empty, and if so, create a ThreadLocalMap:

 void createMap(Thread t, T firstValue) {
        t.threadLocals = new ThreadLocalMap(this, firstValue);
    }

####4.get()

private T setInitialValue() {
        T value = initialValue();
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
        return value;
    }

######Looper saves a reference to the current thread with the following code:

static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>(); // sThreadLocal 为 ThreadLocal 类型

Therefore, the bound thread can be found in the Looper object through sThreadLocal. There is a set method and a get method in ThreadLocal. You can store an object into ThreadLocal through the set method, and then you can retrieve the stored object through the get method.

ThreadLocal uses generics when new. From the above code, we can see that the generic type here is Looper, that is, we can only write and read the Looper object type through the set and get methods of ThreadLocal.

#2 Constructor
The constructor of Looper in the source code is private, that is, a Looper object cannot be obtained in the form of new Looper() outside the class.

private Looper(boolean quitAllowed) {...}

###### Let's take a look at the work of new Looper() in the above code to create a Looper object:

private Looper(boolean quitAllowed) {
    mQueue = new MessageQueue(quitAllowed);  // 创建 MessageQueue 对象
    mThread = Thread.currentThread();        // 记录当前线程
}

Looper.prepare() is only allowed to be executed once per thread. This method will create a Looper object, a MessageQueue object will be created in the Looper's constructor, and then the Looper object will be saved to the current thread TLS.
#1. prepareMainLooper()

In addition, similar to prepare(), there is a prepareMainLooper() method, which is mainly used in the ActivityThread class.

public static void prepareMainLooper() {
        prepare(false);          // 设置不允许退出的 Looper
        synchronized (Looper.class) {
            // 将当前的 Looper 保存为主 Looper,每个线程只允许执行一次
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            sMainLooper = myLooper();
        }
    }

#2.loop(
) The code of Looper.loop() is as follows:

 /**
     * Run the message queue in this thread. Be sure to call
     * {@link #quit()} to end the loop.
     */
    public static void loop() {
        final Looper me = myLooper();            // 获取当前线程绑定的 Looper 对象
        if (me == null) {
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }
        final MessageQueue queue = me.mQueue;    // 获取 Looper 对象中的消息队列

        // Make sure the identity of this thread is that of the local process,
        // and keep track of what that identity token actually is.
        Binder.clearCallingIdentity();
        // 确保在权限检查时基于本地进程,而不是基于最初调用进程
        final long ident = Binder.clearCallingIdentity();

        for (;;) {     // 进入loop的主循环方法
            Message msg = queue.next(); // might block(可能会堵塞)
            if (msg == null) {          // 没有消息,则退出循环
                // No message indicates that the message queue is quitting.
                return;
            }

            // This must be in a local variable, in case a UI event sets the logger
            // 默认为null,可通过 setMessageLogging() 方法来指定输出,用于 debug 功能
            final Printer logging = me.mLogging;
            if (logging != null) {
                logging.println(">>>>> Dispatching to " + msg.target + " " +
                        msg.callback + ": " + msg.what);
            }

            final long slowDispatchThresholdMs = me.mSlowDispatchThresholdMs;

            final long traceTag = me.mTraceTag;
            if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
                Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
            }
            final long start = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
            final long end;
            try {
                msg.target.dispatchMessage(msg);       // 用于分发 Message
                end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
            } finally {
                if (traceTag != 0) {
                    Trace.traceEnd(traceTag);
                }
            }
            if (slowDispatchThresholdMs > 0) {
                final long time = end - start;
                if (time > slowDispatchThresholdMs) {
                    Slog.w(TAG, "Dispatch took " + time + "ms on "
                            + Thread.currentThread().getName() + ", h=" +
                            msg.target + " cb=" + msg.callback + " msg=" + msg.what);
                }
            }

            if (logging != null) {
                logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
            }

            // Make sure that during the course of dispatching the
            // identity of the thread wasn't corrupted.
            final long newIdent = Binder.clearCallingIdentity();     // 确保分发过程中 identity 不会损坏
            if (ident != newIdent) {
                // 打印 identity 改变的 log,在分发消息过程中是不希望身份被改变的
            }

            msg.recycleUnchecked();   // 将 Message 放入消息池
        }
    }

We will focus on analyzing several functions in loop() next:
#####myLooper()
As we said earlier, after looper.prepare() is executed, we can call Looper.myLooper() externally Gets the Looper object bound to the current thread.

public static @Nullable Looper myLooper() {
        return sThreadLocal.get();      // 还是通过 sThreadLocal.get()方法获取当前线程绑定的 Looper 对象
    }

####3.MessageQueue

// Looper 构造函数中创建了 mQueue,即 MessageQueue
private Looper(boolean quitAllowed) {
    mQueue = new MessageQueue(quitAllowed);  // 创建 MessageQueue 对象
    mThread = Thread.currentThread();        // 记录当前线程
}
 public static void loop() {
        final Looper me = myLooper();             // 获取当前线程绑定的 Looper 对象
        if (me == null) {
            ... ...
        }
        final MessageQueue queue = me.mQueue;     // 获取 Looper 对象中的消息队列

The variable me is the Looper bound to the current thread obtained through the static method myLooper(), and me.mQueue is the message queue associated with the current thread.
####4.for()

for (;;) { // 进入loop的主循环方法

We found that the for loop does not set the conditions for the loop to terminate, so this for loop is an infinite loop.

####5.Message

Message msg = queue.next(); // might block

We take a message from the message queue through the next method of the message queue MessageQueue. If there is a Message in the message queue at this time, the next method will return the message immediately. If there is no Message in the message queue at this time, then the next method will block. Waiting to get the Message.

####6.dispatchMessage()

/*package*/ Handler target;
try {
    msg.target.dispatchMessage(msg);
    end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
} finally {
    if (traceTag != 0) {
        Trace.traceEnd(traceTag);
    }
}

The target attribute of msg is Handler. The meaning of this code is to let the Handler associated with the Message let the Handler process the Message through the dispatchMessage method. The dispatchMessage method of the Handler will be introduced in detail below.

####7.recycleUnchecked()

msg.recycleUnchecked(); // 分发后的 Message 回收到消息池,以便重复利用

#Summary
loop() enters loop mode, and repeats the following operations until there is no message to exit the loop:

####1. Read the next Message of MessageQueue;
####2. Distribute the Message to the corresponding target;
####3. Recycle the distributed Message to the message pool for reuse.
Continue to finish the rest tomorrow
image.png

#If you want to learn more Android knowledge, or get relevant information, please join the Android Technology Development Exchange Group 2: 935654177. This group can get free technical tutorials such as Gradle, RxJava, applet, Hybrid, mobile architecture, NDK, React Native, and performance optimization!

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=324160914&siteId=291194637