Handler Handler Handler Handler Handler Handler Handler Handler Handler Handler Handler

Handler handler = new Handler(Looper.getMainLooper());
handler.sendMessage(message);
Copy the code

Constructor analysis

First let’s look at the constructor, the constructor has a lot of overloaded versions, does not call with ginseng, less of the invocation of the multi-parameter, a routine, which is very commonly used such as this is the View of the constructor, this advantage is that, only need at most in the constructor of refs Call a initialization can, Others with few or no parameters will also be initialized to. Now let’s look at the construction with the most arguments:

 public Handler(@NonNull Looper looper, @Nullable Callback callback, boolean async) {
        mLooper = looper;
        mQueue = looper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }
Copy the code

MLooper, mQueue, mCallback, and a mAsynchronous BOOLen value are initialized. MLooper: MQueue: A MessageQueue, which is a queue holding Message mCallback: A callback is used to trigger a callback to the Handler

public interface Callback {
    boolean handleMessage(@NonNull Message msg);
}
Copy the code

IsAsynchronous: a Boolean value that defines whether a message isAsynchronous, more on this later. Ok, now to see where the first looper comes from, click looper.getMainLooper () and you can see:

public static Looper getMainLooper() { synchronized (Looper.class) { return sMainLooper; }}Copy the code

Return sMainLooper and look at where sMainLooper is assigned:

public static void prepareMainLooper() {
    prepare(false);
    synchronized (Looper.class) {
        if (sMainLooper != null) {
            throw new IllegalStateException("The main Looper has already been prepared.");
        }
        sMainLooper = myLooper();
    }
}
Copy the code

We call perpare(false) and initialize it. If it’s already initialized, we throw an exception and assign the value to sMainLooper.

public static @Nullable Looper myLooper() {
        return sThreadLocal.get();
}
Copy the code

SThreadLocal = set(); sThreadLocal = set();

static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>(); 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)); }Copy the code

In prepareMainLooper(), prepare(false) is used to create a Looper and then sthreadLocal.set (Looper) is used. Save to sThreadLocal and assign to sMainLooper using sthreadlocale.get (). Here’s the question: Why not:

sMainLooper = new Looper(quitAllowed);
Copy the code

How easy!! And don’t want to:

sThreadLocal.set(new Looper(quitAllowed));
sMainLooper = sThreadLocal.get();
Copy the code

Question 1: Why does Android store Looper in ThreadLocal? Let’s go back to our main thread. We’ve now gone full circle and found the first Handler’s first argument, mLooper, which is new out here. Let’s look at new Looper(quitAllowed); What has been done:

private Looper(boolean quitAllowed) {
    mQueue = new MessageQueue(quitAllowed);
    mThread = Thread.currentThread();
}
Copy the code

Oh, first: create a MessageQueue(); Second, the current thread is saved. Which thread is this thread? The thread from new Looper(). Prepare () is called in prepareMainLooper(), which means that this thread is the one prepareMainLooper() is called from. So where is prepareMainLooper() called? Oh, here, in the Main method of ActivityThread:

public static void main(String[] args) { ... Omit some code... Looper.prepareMainLooper(); . Omit some code... Looper.loop(); throw new RuntimeException("Main thread loop unexpectedly exited"); }Copy the code

Where is activityThread.main () called? This is called when the current process is created, which is the entry point to the Android application process. We’ll talk about that later. Now that we know that the thread in Looper is this thread, who is this thread? That’s the UI thread, that’s the main thread! Question 2: Where is activityThread.main () called? We found the Handler’s first argument, Looper, which saved the current thread, and new MessageQueue(), so let’s look at the MessageQueue() construction:

MessageQueue(boolean quitAllowed) {
    mQuitAllowed = quitAllowed;
    mPtr = nativeInit();
}
Copy the code

It’s easy to save a single argument and call the nativeInit() method. The Handler’s first argument, mLooper, has been initialized. Now let’s look at the second argument, mQueue:

mQueue= looper.mQueue;
Copy the code

Looper’s mQueue (callback) and looper’s mQueue (callback) have been queued. Create Looper and save it. Create MessageQueue and save


2 Invoke process analysis

How do we use Handler? There are many methods, either: 1 handler.sendMessage(message); Handler.post (runnable), there are other ways to use it, but the truth is the same. Let’s look at the most common: handler.sendmessage (); We’ll start with a simple Message class:

public final class Message implements Parcelable { public int what; Public long when; // Specify when this message will be executed. When this message is executed, int flags will be executed. Handler target; // Which Handler executes the Runnable callback; // Callback Message next; // Point to the next Message *** omit other member variables ***}Copy the code

Click inside to see:

public final boolean sendMessage(@NonNull Message msg) { return sendMessageDelayed(msg, 0); } public final Boolean sendMessageDelayed(@nonnull Message MSG, long delayMillis) { if (delayMillis < 0) { delayMillis = 0; } return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis); } public Boolean sendMessageAtTime(@nonnull Message MSG, long uptimeMillis) { MessageQueue queue = mQueue; 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); // This queue is the same as the Looper in the first step}Copy the code

SendMessageAtTime (MSG, uptimeMillis) : enqueueMessage(queue, MSG, uptimeMillis); Continue to look at:

private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,long uptimeMillis) { msg.target = this; // Set MSG. Target to the current Handler if (mAsynchronous) {// The Handler constructor's fourth argument, MSG. SetAsynchronous (true); } return queue. EnqueueMessage (MSG, uptimeMillis); // This queue is MessageQueue, which is the Looper created by new}Copy the code

The logic is simple: msg.target = this saves the current Handler, and then calls enqueueMessage() of MessageQueue.

Boolean enqueueMessage (Message MSG,long when){// Synchronized (this) {MSG. MarkInUse (); msg.when = when; MSG Message p = mMessages; // This is a member variable: Message mMessages, which holds the header of the current Message queue Boolean needWake; / / whether need to awaken the if (p = = null | | the when = = 0 | | the when < p.w hen) {/ / we look at the if condition: / / 1 p = = null means mMessages = = null; //2 when == 0 is the first message in the enqueue. This is not possible. When is in sendMessageAtTime(). When (MSG) < < mMessages. When (MSG) < mMessages. When (MSG) < mMessages. Msg. next = p; msg.next = p; // Next of MSG is p mMessages = MSG; NeedWake = mBlocked; needWake = mBlocked; needWake = mBlocked; // If the current block, need to wake up, what? //**FLAG1** Look at MessageQueue. Next () and come back!! NeedWake = mBlocked; needWake = mBlocked; needWake = mBlocked; If blocked, you need to wake up, otherwise you don't. FLAG2} else {needWake = mBlocked && p.target == null && msg.isasynchronous (); // Message prev (false); for (; ;) { prev = p; //prev points to queue head p = p.ext; / / team head point to an if (p = = null | | the when < p.w hen) {/ / 1 p = = null, unless it is the last message, namely traversal finished / / 2 when < p.w hen, So this MSG executes a break before p; } if (needWake &&p.isasynchronous ()) {needWake = false; } } msg.next = p; // if MSG is executed before p, insert MSG before p, so msg.next = p; prev.next = msg; // When messageQueue is sorted by the size of when, messageQueue is sorted by the size of when. } if (needWake) {// If (needWake) {// If (needWake) {// If (needWake) {// If (needWake) = mBlocked (mPtr); //**FLAG2** ok if next() does not return MSG; NativePollOnce () is blocked, and nativeWake() is used to wake up messages that need to be processed. EnqueueMessage () and next() are paired together. }} return true;}} return true;}} return true; }Copy the code

If MSG is the first message (p == null), the queue will be inserted into the queue as soon as possible. Or you need to immediately (the when = = 0 | | the when < p.w hen), would immediately wake up, call nativeWake (mPtr) to awaken the native implementation, then the nativeWake (mPtr) is what thing, This can be interpreted as notify() of Java layer. Activitytrhead.main () : MessageQueue; activitytrhead.main () : MessageQueue; activitytrhead.main (); activitytrhead.main ();

public static void main(String[] args) { ... Omit some code... Looper.prepareMainLooper(); . Omit some code... Looper.loop(); Looper. Loop () is a throw new RuntimeException("Main thread loop limited "); }Copy the code

We already know the first sentence: PrepareMainLooper () creates a Looper and creates a MessageQueue. Handler. sendMessage(message) adds a message to the MessageQueue. Now we need to retrieve the message, which must be in looper.loop (). Ok, let’s see.

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; boolean slowDeliveryDetected = false; for (;;) {Message MSG = queue.next(); If (MSG == null) {return; if (MSG == null) {return; // Returns no message, exits the loop, etc. If MSG ==null does not occur, the app will crash. Wait to see if queue.next() returns null} try {// MSG target is the handler for handler.sendMessage(MSG), That is to invoke the handler dispatchMessage (MSG), good we see MSG. Target. DispatchMessage (MSG); } catch (Exception exception) { throw exception; } finally {*** omit other ***} msg.recycleunchecked (); }}Copy the code

Handler. DispatchMessage () as follows:

public void dispatchMessage(@NonNull Message msg) { if (msg.callback ! = null) { handleCallback(msg); } else {if (mCallback! = null) {if (McAllback.handlemessage (MSG)) {if (McAllback.handlemessage (MSG)) {if (McAllback.handlemessage (MSG)); } } handleMessage(msg); // Call our constructor overloaded handlerMessage(MSG) method last,}}Copy the code

If the MSG has no callback, call its own mCallback(if any) first. If the handleMessage(MSG) of the mCallback returns true, Queue.next () will return null if the handleMessage(MSG) Handler is called.

Message next() { final long ptr = mPtr; if (ptr == 0) { return null; // return null!! Oh, my God. What happens? } int nextPollTimeoutMillis = 0; // Record how long it takes for a message to execute for (;;) {// This is an endless loop... Omit some code... / / this can be understood as a Java wait (), said wait for native layer (nextPollTimeoutMillis) time, well, wait for such a long time before / / remember MessageQueue. EnqueueMessage (MSG)? The nativeWake(mPtr) inside is equivalent to noticy(), which is used to wake nativePollOnce(PTR, nextPollTimeoutMillis); synchronized (this) { final long now = SystemClock.uptimeMillis(); PrevMsg = null; Message msg = mMessages; If (MSG!) {if (MSG!) {if (MSG! = null && msg.target == null) { do { prevMsg = msg; msg = msg.next; } while (msg ! = null && ! msg.isAsynchronous()); } // if (MSG! = null) { //msg! If (now < msg.when) {// if (now < msg.when) {// If (now < msg.when) {// If (now < msg.when) {// If (now < msg.when) {// If (now < msg.when) { MSG. When - now = MSG. When - now = MSG. When - now = MSG. Integer.MAX_VALUE is used to prevent malicious transmission of nextPollTimeoutMillis = (int) math. min(msg.when - now, integer.max_value); } else {now >= msg.when, mBlocked = false; // Unblock if (prevMsg! Else prevmsg.next = msg.next; } else {// The queue header points to the next message mMessages = msg.next; } msg.next = null; Msg.markinuse () is deleted from the queue header; // mark return MSG as already used; / / return, well, this time is back to the stars. The loop (), to use this MSG targert. DispatchMessage (MSG)}} else {/ / MSG = = null, that is, the queue is null, direct assignment of 1, said the infinite waiting, NextPollTimeoutMillis = -1; nextPollTimeoutMillis = -1; nextPollTimeoutMillis = -1; } mBlocked = true; // return MSG; So it must be blocked, flag it true, ok, now go to FLAG1... Omit some code... }... Omit code for handling idle messages... }}Copy the code

FLAG3. Okay, now that we’re back from FLAG2, let’s summarize: Looper.loop() is an infinite loop that fetches the MSG from message.next (), and message.next () is an infinite loop that fetches the Message from the current queue head, and returns if the MSG time is up. Int delay = MSG. When – now, call nativePollOnce(delay) to wait, and execute when the wait is over. If there is no message, mBlocked = true is marked as blocked. When a new message enters the queue, enqueueMessage(MSG,when) is used to determine whether there is a message that needs to be executed: If (p = = null | | the when = = 0 | | the when < p.w hen), if there is a message you need to perform, will determine whether blocking, if blocked, will awaken (needWake = mBlocked), otherwise don’t wake up. This section concludes the analysis, and the markup problem in the code will be explained later.

Question 1: Doesn’t MessageQueue sleep indefinitely? In this case, if a new message enters the queue, it must satisfy P == null, that is, it must wake up. Therefore, there is no state of infinite sleep.

Q2: How does Handler implement the delay mechanism? This Handler gets the current time, adds the delay time, saves it to message.when, and compares it to looper.loop(). If it is less than the current time, it needs to be executed immediately. Otherwise, it subtracts: int waitTime = message.when – now; The CPU is then called to wait for waitTime to execute, resulting in a delay

Question 3: When are some asynchronous messages used? This is the VSYNC mechanism used for screen rendering. VSYNC uses a handler to synchronize messages. VSYNC messages are asynchronous and are preferentially processed, as described in the following sections