“This is the 18th day of my participation in the Gwen Challenge in November. Check out the details: The Last Gwen Challenge in 2021”

Principles of Handler mechanism

3.MessageQueue

The previous article introduced enqueueMessage, a method that inserts messages into message queues. There are also the next and quit methods explained in this article.

The next method takes a message from the message queue and removes it from the message queue.

Message next(a) {
    / /...
    int pendingIdleHandlerCount = -1; // -1 only during first iteration
    int nextPollTimeoutMillis = 0;
    for (;;) {
        if(nextPollTimeoutMillis ! =0) {
            Binder.flushPendingCommands();
        }
        // the blocking method Ptr is a long member of a MessageQueue, associated with a C++ layer MessageQueue. It goes to 0 when the queue is abandoned.
        nativePollOnce(ptr, nextPollTimeoutMillis);
​
        synchronized (this) {
            final long now = SystemClock.uptimeMillis();
            Message prevMsg = null;
            Message msg = mMessages;
            if(msg ! =null && msg.target == null) {
                // MSG. Target == null indicates that this message is a message barrier (sent via the postSyncBarrier method)
                // If a message barrier is found, the first asynchronous message (if any) is looped through, and all synchronous messages are ignored.
                do {
                    prevMsg = msg;
                    msg = msg.next;
                } while(msg ! =null && !msg.isAsynchronous());
            }
            if(msg ! =null) {
                if (now < msg.when) {
                    NextPollTimeoutMillis is set if the message has not yet arrived, and nativePollOnce is called to block the message at the start of the next loop.
                    nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                } else {
                    // Fetch the message and set mBlocked = false to indicate that it is not currently blocked.
                    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();
                    returnmsg; }}else {
                nextPollTimeoutMillis = -1;
            }
            if (mQuitting) {
                dispose();
                return null;
            }
            / /... Process IdleHandler tasks}}Copy the code

The nativePollOnce blocking method is mainly implemented through the native layer’s epoll listening for the writing event of the file descriptor. The nextPollTimeoutMillis blocking time will have the following three situations:

  1. If nextPollTimeoutMillis=-1, the block does not time out.
  2. If nextPollTimeoutMillis=0, it does not block and returns immediately.
  3. If nextPollTimeoutMillis>0, block nextPollTimeoutMillis for up to milliseconds (timeout), and return immediately if any program wakes up during that time.

To summarize, mMessages are null when first entered or messages have been processed, meaning there are no messages in the queue. NextPollTimeoutMillis =-1, and goes back to the IdleHandler task. IdleHandler is a Handler mechanism that allows us to execute tasks when we are idle during the Looper event loop.

If a synchronization message is found behind the message barrier callback.

If the message does not arrive, nextPollTimeoutMillis is updated to block.

If so, the message is returned to Looper for processing.