See this article for a basic understanding of Handler, Looper and MessageQueue Interpretation of Android source code - Handler, Loop, MessageQueue
1, How does MessageQueue implement Message addition and delay acquisition?
1, How to add
enqueueMessage
boolean enqueueMessage(Message msg, long when) { if (msg.target == null) { throw new IllegalArgumentException("Message must have a target."); } synchronized (this) { ...... msg.when = when; Message p = mMessages; boolean needWake; //---------------------------------------1 if (p == null || when == 0 || when < p.when) { // New head, wake up the event queue if blocked. msg.next = p; mMessages = msg; needWake = mBlocked; } else { //----------------------------------------2 // Inserted within the middle of the queue. Usually we don't have to wake // up the event queue unless there is a barrier at the head of the queue // and the message is the earliest asynchronous message in the queue. 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; } // We can assume mPtr != 0 because mQuitting is false. if (needWake) { nativeWake(mPtr); } } return true; }
From the above code, we can know that there are two ways to add Message
First case
If the when of the current Message to be added is equal to 0 or less than the when of the header Message, directly add the Message to be added to the queue header.
If the original queue is like this

Now add a new Message when=1

Look at the above source code, which can be divided into two cases.
1. If mMessages==null
This is the first time to add a Message. If the original queue is empty, the queue content is msg.
2.when ==0|| when < p.when
This situation is as shown in the figure above. When mMessages is not empty, msg's next points to mMessages, that is, msg is used as the header, and the one with a small when value is placed in the header. The results are as follows

By mMessages = msg; As you can see, mMessages is the right one.
The second case
when > p.when
Suppose the queue is as follows

We will now add Message when=6

At this time, we will traverse the queue and always find the Message whose when of msg is greater than the next maximum when value in the current queue, or when the maximum when cannot be found, we will directly add it to the Message or tail of the next maximum when. give the result as follows

Who is the target of the mMessages at this time? It depends on the next method.
2, How to delay acquisition
Message next() { // Return here if the message loop has already quit and been disposed. // This can happen if the application tries to restart a looper after quit // which is not supported. final long ptr = mPtr; if (ptr == 0) { return null; } int pendingIdleHandlerCount = -1; // -1 only during first iteration int nextPollTimeoutMillis = 0; for (;;) { if (nextPollTimeoutMillis != 0) { Binder.flushPendingCommands(); } 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) { // Stalled by a barrier. Find the next asynchronous message in the queue. do { prevMsg = msg; msg = msg.next; } while (msg != null && !msg.isAsynchronous()); } if (msg != null) { if (now < msg.when) { // Next message is not ready. Set a timeout to wake up when it is ready. nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE); } else { // Got a message. 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 { // No more messages. nextPollTimeoutMillis = -1; } // Process the quit message now that all pending messages have been handled. if (mQuitting) { dispose(); return null; } // If first time idle, then get the number of idlers to run. // Idle handles only run if the queue is empty or if the first message // in the queue (possibly a barrier) is due to be handled in the future. 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); } } } // Reset the idle handler count to 0 so we do not run them again. pendingIdleHandlerCount = 0; // While calling an idle handler, a new message could have been delivered // so go back and look again for a pending message without waiting. nextPollTimeoutMillis = 0; } }
1. Every time Message is taken as Message msg = mMessages, it is the right header Message.
2. We know from this article that the final method of adding Message to Handler is to call sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis); This method knows that the when value = systemclock uptimeMillis() + delayMillis.
The main parameter of next is nextPollTimeoutMillis, which determines when message can be retrieved. nextPollTimeoutMillis = MSG when - now.
Call nativePollOnce(ptr, nextPollTimeoutMillis) to block. This is a local method that will call the underlying C + + code. The C + + code will eventually listen for the write event of the file descriptor through the epoll of Linux to realize the delay.
1. If nextPollTimeoutMillis=-1, msg is empty at this time, and the blocking will not timeout.
2. If nextPollTimeoutMillis=0, it will not block and return immediately.
3. If nextPollTimeoutMillis > 0, the longest blocking time is nextPollTimeoutMillis (timeout). If there is program wake-up during this period, it will return immediately.
2, Looper Loop is an endless loop. If you can't get the Message to be processed, it will be blocked. Why won't ANR be caused in the UI thread?
In ActivityManagerService, we found calling process Start interface to create a new process, which will be imported into Android app. The ActivityThread class and executes its main function. ActivityThread does not inherit from the Thread. It is not the main Thread. I guess the current process is the main Thread.
1. Why is it blocked
A thread. If the execution is completed, the thread will die. If the current Looper is not blocked and the execution is completed, the app burps directly.
2. Why can I jump to the page after blocking
This question is two questions. The first is how to perform page Jump and other operations. The second is why you can operate the page after blocking.
First, we found thread in the ActivityThread main function attach(false, startSeq); Behavior, which is to create a binder thread to make the current process and system_server system process communication.
For example, to start a new Activity:
1. The MainActivity of the application notifies the ActivityManagerService through the Binder interprocess communication mechanism that it wants to start a new Activity;
2.ActivityManagerService notifies MainActivity to enter the paid state through Binder inter process communication mechanism;
3.MainActivity notifies ActivityManagerService through Binder inter process communication mechanism that it is ready to enter the Paused state;
4.ActivityManagerService notifies the ActivityThread of MainActivity through Binder inter process communication mechanism. Now everything is ready and it can really start the Activity.
Second, it can operate after blocking, jump to the page, and call various declaration cycles. We can see that the ActivityThread defines the inner class Handler, which and looper are in the same thread, that is, the main thread. The Handler can accept data from the binder thread, such as msg=H.LAUNCH_ACTIVITY is added to the MessageQueue. Looper monitors that there is data, sends the message through the Handler, and finally calls ActivityThread Handlelaunchactivity () method creates an activity instance through the reflection mechanism, and then executes the activity Oncreate() and other methods;
3. Where in the source code does the ANR report an error
From the source code, we can know
- The user's input was not responded by App within 5s
- onReceiver() of BroadcastReceiver exceeds 10s
- Each life cycle function in the Service has been executed for more than 20s
These three situations will be reported to ANR.