Android messaging is probably pretty poorly written, but it’s worth it anyway, because Android applications are message-driven, and Android is a message-driven system in a sense, with UI, events, and life cycles all tied to messaging. And message processing mechanism is particularly important in the whole Android knowledge system, in too many articles about source code analysis more cumbersome, a lot of people to the entire message processing mechanism is still puzzled, this article through the mode of some questions and answers in combination with Android main thread thread (UI), to explain to the working principle of the source code comments is very full, Also combined with flow charts, if you are not very understanding of Android message processing mechanism, I believe that as long as you calm down and patience to see, there will be a lot of harvest.

1. What is Android message processing?

Message processing mechanism essence: a thread in loop mode continuously listens and processes messages sent to it by other threads in turn.

To put it simply: A thread on an infinite loop pattern, continue to traverse the message list, if there is news will turn out for processing, if the list no news, is blocked (equivalent to wait, yield the CPU resources to other threads), other threads if you want this thread to do anything, just insert into the thread’s message queue, The thread is constantly pulling messages from the queue for processing.

2. How does Android message processing work?

An analogy: the company analogy App

  • The main work of PM is to design products, write requirements documents, change requirements, change requirements in the middle of the process, change requirements before testing…
  • The main work of UI is UI design, interaction and so on.
  • RD work I will not say
  • The CEO doesn’t explain.

After the company starts (App starts), the CEO gets to work (main thread [UI thread] starts), and the CEO is in endless workaholic mode, The CEO hired an RD (New Handler instance) and told the PM and UI to let me know if you have any tasks or requirements. RD will write the PM and UI requirements into the CEO’s memo one by one. The job of the CEO infinite loop is to constantly check the memo to see what needs to be done, take tasks out of the memo one by one, and hand them off to the RD to handle. . Of course, if the memos are all done, then the CEO will go to sleep. But a special feature of this memo is that when the first task is suddenly inserted (from nothing), it will have an alarm function to wake the CEO up and continue working on the memo. This is basically how the whole message processing mechanism works. There will be source code analysis later, and you will understand it better if you combine this scenario.

Here is a flow chart of Android message processing mechanism and specific execution animation. If you don’t understand it, you can read it below (the Android UI main thread will be explained later). Then you can understand the implementation principle of the whole mechanism better by combining the diagram and animation.







3, Looper, Handler, MessageQueue,Message function and existence significance?

  • Looper We know that a thread is a piece of executable code. When the executable code is finished, the thread life cycle will terminate and the thread will exit. Then, as the main thread of App, what will happen if the code section is finished? , the App will automatically exit after executing a piece of code after starting, which is very unreasonable. So in order to prevent the execution of the code section, you can only insert an infinite loop into the code, so the code will not be executed, and then automatically exit. How to insert an infinite loop into the code? Call Looper. Prepare () on the main thread… Looper.loop() transforms the current thread into a Looper thread. Looper.loop() has an infinite loop, so the main thread enters while(true){… } code can’t jump out, but the main thread can’t do nothing, right? While (true){… }, inside the main thread will wait for other threads continuously in the infinite loop to its message (message includes: the Activity start, life cycle, update the UI, the control events, etc.), have the news, according to a news do the corresponding processing, of which the other part of the job is in cyclic code will continue to turn out messages from the message queue for the main thread processing.

  • MessageQueue The reason for the existence of MessageQueue is very simple, that is, the same thread can only process one message at a time, the same thread code execution is not concurrent, so it needs queues to store messages and arrange the processing order of each message. Multiple threads send messages to the UI thread, and the UI thread has to keep those messages in a list (it can’t handle that many tasks at a time), and then pull them out one by one. It’s a simple design, and we often do that when we write code. Each Looper thread maintains such a queue, and only one queue, whose messages can only be processed by that thread.

  • Handler Handler is used to communicate between threads of the same process. Looper lets the main thread loop indefinitely from its own MessageQueue. Since we know that the processing of messages must be done in the main thread, how can other threads put messages in the main thread queue? In fact, it is very simple, we know that resources are shared between threads and threads in the same process, that is, any variable can be accessed and modified in any thread, as long as we consider the concurrency and synchronization, so as long as we get the MessageQueue instance, we can put messages into the MessageQueue of the main thread. The main thread processes this message in the main thread when polling. MessageQueue (mLooper = looper.mylooper (); mQueue = mLooper.mQueue;) But Google also built a Handler class to add messages and handle callback messages. You just build the Handler class on the main thread. This Handler instance gets a reference to the main MessageQueue instance (mLooper = looper.mylooper (); MQueue = mLooper. MQueue) Handler inserts a new message into the message queue using this reference when sendMessage is sent. Another function of the Handler is to uniformly handle message callbacks. It’s nice to have a Handler that sends a message and then ensures that the message processing is done by itself. The Message in the queue holds a reference to Handler (which Handler puts it on the queue, Message holds a reference to Handler), and when the main thread polls the Message, Call back and forth to the handleMessage(Message MSG) method of the Handler that we often overwrite.

  • Message is very simple, you always have to tell the main thread what you want it to do, you always have to pass some data to it, Message is the carrier.

Next, we will explain the main thread (UI thread) of the App, and analyze the entire Android message processing mechanism step by step from the start of the App. First, there is the familiar function of Main in the ActivityThread class. The entry of the code for the start of the App is here, and the UI thread is just a common thread. This is where the UI thread is converted into a Looper thread, and we’ll see what a Looper thread is.

public final class ActivityThread { public static final void main(String[] args) { ...... Looper.prepareMainLooper(); . ActivityThread thread = new ActivityThread(); thread.attach(false); if (sMainThreadHandler == null) { sMainThreadHandler = thread.getHandler(); }... Looper.loop(); . }}Copy the code

The first thing we do is looper.prepareMainLooper (). Let’s see what this method inLooper does.

Note: What is ThreadLocal before you look at it? ThreadLocal: Each Thread has its own private Local Storage area. Different threads cannot access each other’s TLS area. The thread’s own local storage area is the thread’s own Looper. Threadlocal.java (threadlocal.java)

Public final class Looper {// sThreadLocal is static. // Then you can use sThreadLocal to get the current thread's owning Looper. static final ThreadLocal sThreadLocal = new ThreadLocal(); The main thread (UI thread) Looper is handled separately, which is static, and can be easily obtained by using getMainLooper() private static Looper sMainLooper; // the MessageQueue of the thread to which Looper belongs final MessageQueue mQueue; // The Thread to which Looper belongs final Thread mThread; public static void prepare() { prepare(true); } private static void prepare(Boolean quitAllowed) {// If a thread has TLS data, an exception is raised. if (sThreadLocal.get() ! = null) { throw new RuntimeException("Only one Looper may be created per thread"); } // Insert data into Thread TLS. Map.put (thread.currentThread (),new Looper(quitAllowed)); sThreadLocal.set(new Looper(quitAllowed)); } // It actually calls prepare(false) and then assigns sMainLooper. public static void prepareMainLooper() { prepare(false); synchronized (Looper.class) { if (sMainLooper ! = null) { throw new IllegalStateException("The main Looper has already been prepared."); } sMainLooper = myLooper(); }} public static Looper getMainLooper() {synchronized (looper.class) {return sMainLooper;  }} public static @nullable Looper myLooper() { Map.get (thread.currentThread ())) returns sthreadLocal.get (); }}Copy the code

PrepareMainLooper () creates a new Looper instance and puts it into a static ThreadLocal sThreadLocal variable under the Looper class. We also assign sMainLooper, which is to get the main thread Looper quickly through looper.getMainLooper (). SMainLooper is the main thread Looper which may be fetched frequently, Avoid going to sThreadLocal every time.

The next thing I want to do is look at the Looper constructor and see what happens when I do new Looper.

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

See, the MessageQueue MessageQueue is created for the current thread and Looper holds a reference to MessageQueue. After looper.prepareMainLooper (), the main thread is converted from a normal thread to a Looper thread. The main thread already has a Looper object and a message queue, mQueue, with the following reference relationship :(the main thread can easily get its Looper, and the main thread’s Looper holds a reference to the main thread’s message queue)




How do I get the main thread’s Looper object and message list?

// Execute mLooper = looper.mylooper (); MQueue = mlooper.mqueue // or mLooper= looper.getMainLooper ()Copy the code

After looper.prepareMainLooper (), ActivityThread thread = new ActivityThread(); Public final Class ActivityThread{public final class ActivityThread{public final class ActivityThread{public final class ActivityThread{public final class ActivityThread{… The constructor for ActivityThread does nothing but initialize the resource manager.

 ActivityThread() {
     mResourcesManager = ResourcesManager.getInstance();
 }Copy the code

Move on to the next line of code

ActivityThread thread = new ActivityThread(); Attach (false); // Create a new thread.Copy the code

thread.attach(false); A Binder thread is created (ApplicationThread, which sends messages to the main thread via the Handler, more on that later). We mentioned earlier that the main thread ends up in an infinite loop. If no other thread is created before the loop ends, who will send messages to the main thread later? That’s right, that’s where this thread is created, and this thread is going to receive some events from the system service that encapsulate the Message and send it to the main thread, and the main thread is going to take those messages from the queue in an infinite loop and process them. (LAUNCH_ACTIVITY, PAUSE_ACTIVITY, etc.)

Looper.loop() = looper.loop () = looper.loop ()

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; // Get the message queue in the Looper object.... for (;;) {Message MSG = queue.next(); If (MSG == null) {if (MSG == null) {if (MSG == null) {return; }... MSG. Target is a Handler object that sends the Message to the queue. The Message holds a reference to the Handler. And put it in its own target variable to call back to the handleMessage method of the // handler we overwrote. msg.target.dispatchMessage(msg); . . msg.recycleUnchecked(); // Retrieve Message to the Message pool, obtain() instead of recreating it the next time it is used. }}Copy the code

The code above, if you look at the comments, is that the main thread, the UI thread, at this point, is in an infinite loop, constantly picking up messages from the message queue, right? The UI thread is stuck in the loop, and the main thread is not stuck because of the loop in looper.loop ().

  • After Looper starts, any code executed on the main thread is pulled from the message queue by Looper to execute. That is, the main thread is then sent messages by other threads to perform other operations. The same is true for life cycle callbacks. The System service ActivityManagerService sends an IPC call to the APP process through its Binder, which inserts a message into the main thread’s message queue through the APP process’s Binder thread.

2. The main thread is the UI thread that interacts with the user. It should have a high priority. What about the other threads of the App process?

  • This is basically a producer-consumer model. If there is no message in the main thread’s MessageQueue, it will block in the loop’s queue.next() method. At this point, the main thread will release CPU resources and sleep until the next message comes in and wakes up the main thread. Prior to version 2.2, this mechanism was implemented using the familiar wait and notify threads, and later versions involved the Linux PIPE /epoll mechanism, which woke up the main thread by writing data to the pipe end. The principle is similar to that of I/O. Read and write are blocked and do not occupy CPU resources.

So the above code focuses on the queue.next() function, and we won’t talk about the rest, let’s look at the source of queue.next() (mainly look at the comments) :

Message next() final long ptr = mPtr; if (ptr == 0) { return null; } int pendingIdleHandlerCount = -1; NextPollTimeoutMillis indicates that the nativePollOnce method needs to wait for nextPollTimeoutMillis before returning an int nextPollTimeoutMillis = 0; for (;;) { if (nextPollTimeoutMillis ! = 0) { Binder.flushPendingCommands(); } // If the nextPollTimeoutMillis is waiting for the nextPollTimeoutMillis, the nextPollTimeoutMillis is waiting for the nextPollTimeoutMillis, the nextPollTimeoutMillis is waiting for the nextPollTimeoutMillis, the nextPollTimeoutMillis is waiting for the nextPollTimeoutMillis. NativePollOnce also returns 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 ! Target == null) {// Loop to find a non-asynchronous message do {prevMsg = MSG; msg = msg.next; } while (msg ! = null && ! msg.isAsynchronous()); } if (msg ! NextPollTimeoutMillis = null) {if (now < msg.when) {// If (now < msg.when) {// If (now < msg.when) {// If (now < msg.when) { NextPollTimeoutMillis = (int) math.min (MSG. When - now, Integer.MAX_VALUE); } else {// The message mBlocked = false; // List some operations, get MSG and delete the node if (prevMsg! = null) prevMsg.next = msg.next; } else { mMessages = msg.next; } MSG. Next = null; msg.markInUse(); Return MSG; NextPollTimeoutMillis = -1; nextPollTimeoutMillis = -1; }... . }Copy the code

NativePollOnce () is a native function, which does a lot of work in Native, mainly involving the processing of epoll mechanism (blocking at the reading end of the pipeline when there is no message processing). The specific source code related to Native is not covered in this article. Interested students can find it online. Many of them go deeper.

Create stars analysis here, from the start, create a message queue, to enter the loop method performs an infinite loop, this piece of came to an end, in infinite loop have been the main thread polling waiting for the news, then we will see again, system is how to send a message to the main thread, the main thread is how to deal with these news?

When an Activity is ready to start, the ActivityManagerService (AMS) thread of the system service process sends an IPC call to the APP process through its Binder. Binder threads in the App process finally call the scheduleLaunchActivity method below the ActivityThread class to prepare the Activity to start.

Binder threads are applicationThreads that receive information from system processes in App processes (they are created before the main thread enters an infinite loop).

// This method is not called on the main thread, Public final void scheduleLaunchActivity(Intent Intent, IBinder Token, int ident, ActivityInfo, Configuration curConfig, Configuration overrideConfig, CompatibilityInfo compatInfo, String referrer, IVoiceInteractor voiceInteractor, int procState, Bundle state, PersistableBundle persistentState, List pendingResults, List pendingNewIntents, boolean notResumed, boolean isForward, ProfilerInfo profilerInfo) { updateProcessState(procState, false); ActivityClientRecord r = new ActivityClientRecord(); r.token = token; r.ident = ident; r.intent = intent; r.referrer = referrer; r.voiceInteractor = voiceInteractor; r.activityInfo = info; r.compatInfo = compatInfo; r.state = state; r.persistentState = persistentState; r.pendingResults = pendingResults; r.pendingIntents = pendingNewIntents; r.startsNotResumed = notResumed; r.isForward = isForward; r.profilerInfo = profilerInfo; r.overrideConfig = overrideConfig; updatePendingConfiguration(curConfig); sendMessage(H.LAUNCH_ACTIVITY, r); }Copy the code

After wrapping some startup information into ActivityClientRecord, the last sentence calls sendMessage(h.launch_activity, r); Let’s move on:

private void sendMessage(int what, Object obj) {
        sendMessage(what, obj, 0, 0, false);
    }
private void sendMessage(int what, Object obj, int arg1, int arg2, boolean async) {
         Message msg = Message.obtain();
        msg.what = what;
        msg.obj = obj;
        msg.arg1 = arg1;
        msg.arg2 = arg2;
        if (async) {
            msg.setAsynchronous(true);
        }
        mH.sendMessage(msg);
    }Copy the code

See, the last Message that starts the Activity is wrapped with a Message, but there is a problem here. In the analysis of the main function, there is no way to insert a Message into the main Message queue. There is a Message, but how to send it to the main Message queue? MH. SendMessage (MSG); What is mH? Is it Handler? Let’s see what it is? We take a look at the ActivityThread member variable and find a line of initialization code

final H mH = new H();Copy the code

So let’s keep going. What is H?

public final class ActivityThread{ .... final H mH = new H(); . private class H extends Handler { .... . public void handleMessage(Message msg) { if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what)); switch (msg.what) { case LAUNCH_ACTIVITY: { Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStart"); final ActivityClientRecord r = (ActivityClientRecord) msg.obj; r.packageInfo = getPackageInfoNoCheck( r.activityInfo.applicationInfo, r.compatInfo); handleLaunchActivity(r, null); Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); } break; case RELAUNCH_ACTIVITY: { Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityRestart"); ActivityClientRecord r = (ActivityClientRecord)msg.obj; handleRelaunchActivity(r); Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); } break; case PAUSE_ACTIVITY: Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityPause"); handlePauseActivity((IBinder)msg.obj, false, (msg.arg1&1) ! = 0, msg.arg2, (msg.arg1&2) ! = 0); maybeSnapshot(); Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); break; . }... . }}Copy the code

LAUNCH_ACTIVITY, PAUSE_ACTIVITY, RESUME_ACTIVITY, RESUME_ACTIVITY… So many, many, many, many, many, many, many, many, many, many, many, many, many, many, many, many. Return to mh.sendMessage (MSG) at the Binder thread. The Handler mH instance created by the main thread sends messages to the message queue of the main thread. The message queue is created from nothing, the main thread is woken up, the main thread loop gets the message, and the handleMessage method of the main thread is called back to handle the LAUNCH_ACTIVITY and other messages. Thus realizing the start of the Activity.

At this point, the basic analysis of a startup process is finished, we may want to know is mH. SendMessage (MSG); How does the Hanlder send messages to the message queue of the main thread? Handler Handler Handler Handler Handler Handler Handler Handler Handler The Handler(Callback Callback, Boolean async) constructor is called. The Handler(Callback Callback, Boolean async) constructor is called.

public Handler() { this(null, false); } public Handler(Callback Callback, Boolean async) { if (FIND_POTENTIAL_LEAKS) { final Class klass = getClass(); if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) && (klass.getModifiers() & Modifier.STATIC) == 0) { Log.w(TAG, "The following Handler class should be static or leaks might occur: " + klass.getCanonicalName()); }} // Get the current thread's Looper. Remember the looper.mylooper () method? Map.get (thread.currentthread ()) // get the currentThread's Looper mLooper = looper.mylooper (); If (mLooper == null) {// The current thread is not a Looper thread, Throw new RuntimeException("Can't create handler inside thread that has not been called Looper.prepare()"); } // let Handler hold a reference to the current thread message queue mQueue = mlooper.mqueue; MCallback = callback; mCallback = callback; mCallback = callback; mAsynchronous = async; }Copy the code

The Handler holds a reference to the thread’s Looper and a reference to its message queue in the thread on which the Handler object is built. Holding a reference to this thread’s message queue means that the Handler object can add messages to that thread’s message queue in any other thread, and also means that the Handler’s handlerMessage must be executed in that thread. 2. If the thread is not a Looper thread, the new Handler will report an error in that thread! Looper.prepare(); looper.loop (); looper.prepare ();

Class LooperThread extends Thread {class LooperThread extends Thread {// Other threads can add messages to the Thread's message queue via mHandler. public void run() { Looper.prepare(); // Before the thread enters an infinite loop, MHandler = new Handler() {public void handleMessage(Message MSG) {//Handler object is constructed in this thread, The handleMessage method is executed on this thread}}; Looper.loop(); }}Copy the code

So let’s move on to Handler’s sendMessage(MSG) method, which is one of the more important and commonly used methods, Handler has a number of methods starting with sendXXXX, sendMessageAtTime, sendEmptyMessageDelayed, sendEmptyMessage, etc., which are used to add messages to message queues, These methods will eventually call enqueueMessage to queue messages:

Private Boolean enqueueMessage(MessageQueue Queue, Message MSG, long uptimeMillis) {private Boolean enqueueMessage(MessageQueue Queue, Message MSG, long uptimeMillis) // Call handler's handleMessage method MSG. Target = this when the message is polled by the Looper thread. if (mAsynchronous) { msg.setAsynchronous(true); } return queue.MessageQueue(MSG, uptimeMillis); }Copy the code

MessageQueue(MSG, uptimeMillis)

boolean enqueueMessage(Message msg, If (MSG. Target == null) {throw new IllegalArgumentException("Message must have a target."); } if (msg.isInUse()) { throw new IllegalStateException(msg + " This message is already in use."); } // The message queue needs to be synchronized. Synchronized (this) {if (McOntract) {IllegalStateException e = new IllegalStateException( msg.target + " sending message to a Handler on a dead thread"); Log.w(TAG, e.getMessage(), e); msg.recycle(); return false; } msg.markInUse(); // When indicates when the message is executed, When = systemclock. uptimeMillis()+delayMillis msg.when = when; Message p = mMessages; boolean needWake; If (p = = null | | the when = = 0 | | the when < p.w hen) {/ / p = = null said MSG. The current message queue no next = p; mMessages = msg; NeedWake = mBlocked; needWake = mBlocked; needWake = mBlocked; } else { // 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; // Put the message in the queue. The queue is sorted by MSG when. { 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; If (needWake) {nativeWake(mPtr); if (needWake) {nativeWake(mPtr); } } return true; }Copy the code

Finally, we look at the dispatchMessage method of the Handler, the method in which the thread out of the message queue, through MSG. Target. DispatchMessage (MSG) call.

// public void dispatchMessage(MSG) {// If (MSG. = null) { handleCallback(msg); } else { if (mCallback ! = null) { if (mCallback.handleMessage(msg)) { return; }} // The handleMessage(MSG) method we overwrote will be called; }}Copy the code

At this point, the entire Android message processing mechanism Java layer content is basically explained (welcome to my brief book: Kelin)

Note: [reproduced please indicate, problems can leave a message, error hope pointed out, like can reward, blog continues to update, welcome to pay attention to]