Related recommendations:

  • The Android source code to read | Handler
  • The Android source code to read | stars
  • The Android source code to read | MessageQueue
  • The Android source code to read | Message
  • Android custom View | distortion effect

Class notes

/**
 * Low-level class holding the list of messages to be dispatched by a
 * {@link Looper}.  Messages are not added directly to a MessageQueue,
 * but rather through {@link Handler} objects associated with the Looper.
 * 
 * <p>You can retrieve the MessageQueue for the current thread with
 * {@link Looper#myQueue() Looper.myQueue()}.
 */
Copy the code
  • MessageQueue holds the list of messages sent by Looper
  • Messages are not added directly to MessageQueue, but via the Handler object associated with Looper.
  • Access MessageQueue via looper.myQueue ()

Insert message enqueueMessage

In the handler class, there is a method that looks like this:

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
    msg.target = this;
    if (mAsynchronous) {
        msg.setAsynchronous(true);
    }
    return queue.enqueueMessage(msg, uptimeMillis);
}
Copy the code

Queue.enqueuemessage inserts a message to the message queue. EnqueueMessage:

Message mMessages; Boolean enqueueMessage(Message MSG, long WHEN) {// Check the validity code omitted... Synchronized (this) {//quit... msg.markInUse(); msg.when = when; Message p = mMessages; boolean needWake; / / 【 1 】 the if (p = = null | | the when = = 0 | | the when < p.w hen) {MSG. Next = p; mMessages = msg; needWake = mBlocked; } else { needWake = mBlocked && p.target == null && msg.isAsynchronous(); Message prev; //【2】 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; } //wake code omitted... } return true; }Copy the code

Some unimportant code was omitted. At the very beginning, we see a variable called Message mMessages, which is explicitly the queue header. In the source code analysis of the Message class, we know:

// sometimes we store linked lists of these things
/*package*/ Message next;
Copy the code

Next: messageQueue: messageQueue: messageQueue: messageQueue: messageQueue: messageQueue: messageQueue: messageQueue The same routine maintains messageQueue in the form of a linked list.

[1] in tag 1, if it is judged that the current queue header is empty, or the time of the message to be inserted is equal to 0, or the message is less than the current queue header, then the message is inserted to the first. Why, it seems to have put the “when” in the front. Let’s move on

[2] In tag 2, the for loop iterates through the message of the entire list queue. When iterating to the position when < P. hen, or the position at the end of the queue where P ==null, it finishes iterating and inserts into the corresponding position. The queue is sorted from smallest to largest by the time when the message is processed.

RemoveMessages removeMessages

void removeMessages(Handler h, int what, Object object) { if (h == null) { return; } synchronized (this) { Message p = mMessages; // Remove all messages at front. while (p ! = null && p.target == h && p.what == what && (object == null || p.obj == object)) { Message n = p.next; mMessages = n; p.recycleUnchecked(); p = n; } // Remove all messages after front. while (p ! = null) { Message n = p.next; if (n ! = null) { if (n.target == h && n.what == what && (object == null || n.obj == object)) { Message nn = n.next; n.recycleUnchecked(); p.next = nn; continue; } } p = n; }}}Copy the code

The first while is deleted from the head of the queue. If the head is deleted, it points to the next element and repeats the process until a mismatched element appears, jumps out of the while, and then iterates backwards from that element, looking for matching messages and deleting them to the end of the queue. Remove has multiple overloads, but the logic is similar.

Get the next message next()

Message next() {// omit some code... int pendingIdleHandlerCount = -1; // -1 only during first iteration int nextPollTimeoutMillis = 0; for (;;) {// omit some code... synchronized (this) { final long now = SystemClock.uptimeMillis(); Message prevMsg = null; Message msg = mMessages; // omit some code... if (msg ! = null) { if (now < msg.when) { nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE); } else { mBlocked = false; if (prevMsg ! = null) { prevMsg.next = msg.next; } else { mMessages = msg.next; } msg.next = null; msg.markInUse(); return msg; } } else { nextPollTimeoutMillis = -1; } // Exit if (McOntract) {dispose(); return null; } // If no message is received, it is idle. And then fell back and forth below IdleHandler interface if (pendingIdleHandlerCount < 0 && (mMessages = = null | | now < mMessages. When)) { pendingIdleHandlerCount = mIdleHandlers.size(); } if (pendingIdleHandlerCount <= 0) { // No idle handlers to run. Loop and wait some more. mBlocked = true; continue; } if (mPendingIdleHandlers == null) { mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)]; } mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers); } // Run the idle handlers. // We only ever reach this code block during the first iteration. for (int i = 0; i < pendingIdleHandlerCount; i++) { final IdleHandler idler = mPendingIdleHandlers[i]; mPendingIdleHandlers[i] = null; // release the reference to the handler boolean keep = false; try { keep = idler.queueIdle(); } catch (Throwable t) { Log.wtf(TAG, "IdleHandler threw exception", t); } if (! keep) { synchronized (this) { mIdleHandlers.remove(idler); }}} // omit some code... }}Copy the code

Final Long now = systemclock. uptimeMillis(); Message MSG = mMessages; If the current time is less than the time of the header message, now < msg.when if not, return the header and point it to the next one.

If no message is received at the end, the queue is empty and the following logic is entered:

for (int i = 0; i < pendingIdleHandlerCount; i++) { final IdleHandler idler = mPendingIdleHandlers[i]; mPendingIdleHandlers[i] = null; // release the reference to the handler boolean keep = false; try { keep = idler.queueIdle(); } catch (Throwable t) { Log.wtf(TAG, "IdleHandler threw exception", t); } if (! keep) { synchronized (this) { mIdleHandlers.remove(idler); }}}Copy the code

What is this IdleHandler thing?

/** * Callback interface for discovering when a thread is going to block * waiting for more messages. */ public static interface IdleHandler { /** * Called when the message queue has run out of messages and will now * wait for more. Return  true to keep your idle handler active, false * to have it removed. This may be called if there are still messages * pending in the queue, but they are all scheduled to be dispatched * after the current time. */ boolean queueIdle(); }Copy the code

As you can see from the comment, this interface will call back when there is no message (message queue is empty), that is, when the queue is idle, the interface will be called back. This interface returns a Boolean value, true meaning that the IdleHandler interface is not removed after a single execution, false meaning that the IdleHandler interface is removed.

In Android, we can process messages, which we can execute immediately or delay for a certain amount of time. After the Handler thread has finished executing all the messages, it waits, blocking until a new Message arrives. If so, then the thread is too wasteful. MessageQueue provides another class of messages, IdleHandler.

IdleHandler allows you to do extra things when the MessageQueue is idle. For example, you can do this: IdleHandler, page startup optimization artifact, pro test, startup speed did improve a lot.

AddIdleHandler (new messagequeue.idleHandler () {@override public Boolean queueIdle() {// Start loading data return false; }});Copy the code

End the quit ()

void quit(boolean safe) { if (! mQuitAllowed) { throw new IllegalStateException("Main thread not allowed to quit."); } synchronized (this) { if (mQuitting) { return; } mQuitting = true; if (safe) { removeAllFutureMessagesLocked(); } else { removeAllMessagesLocked(); } // We can assume mPtr ! = 0 because mQuitting was previously false. nativeWake(mPtr); }}Copy the code

If safe = false is not safe, execute the following method to remove all messages from the queue and recycle them.

private void removeAllMessagesLocked() { Message p = mMessages; while (p ! = null) { Message n = p.next; p.recycleUnchecked(); p = n; } mMessages = null; }Copy the code

But if safe = true, do the following:

private void removeAllFutureMessagesLocked() {
    final long now = SystemClock.uptimeMillis();
    Message p = mMessages;
    if (p != null) {
        if (p.when > now) {
            removeAllMessagesLocked();
        } else {
            Message n;
            for (;;) {
                n = p.next;
                if (n == null) {
                    return;
                }
                if (n.when > now) {
                    break;
                }
                p = n;
            }
            p.next = null;
            do {
                p = n;
                n = p.next;
                p.recycleUnchecked();
            } while (n != null);
        }
    }
}
Copy the code

When the header’s message processing time is longer than the present time, it indicates that the header’s message will be processed in the future, and because the queue is arranged according to the execution time, it also indicates that the whole message queue should be processed in the future. In this case, it also directly discarded all messages. But if the header processing time is smaller than it is now, the search continues until n.hen >now is found, and from there, the message is recycled.

In other words, when quitting, if there are unprocessed messages, they must be directly discarded (removeAllFutureMessages).

conclusion

  • Message queues are implemented based on linked lists
  • The message queue is ordered, in order of execution time, from smallest to largest, the header messages will be taken out and executed first, the first to be processed
  • Next () gets the next message by looping to see if there are any eligible messages in the current queue (now >= msg.when). IdleHandler’s interface is called back when the queue is idle
  • The next() method continually compares the difference with the current time and executes the message when msg.when <= now is determined.
  • Quit () terminates the queue loop, throwing away all unprocessed messages (future execution messages).

Code word is not easy, convenient if the quality of three even, or pay attention to my public number technology sauce, focus on Android technology, not regularly push fresh articles, if you have a good article to share with you, welcome to pay attention to contribute!