preface

As we all know, GUI applications are message (event) driven, and so are Android applications. Message delivery depends on the message mechanism provided by the application framework.

Android actually provides two messaging mechanisms:

Inter-component messages: Intent mechanisms

Inter-thread messaging: Message mechanism

This article will discuss the same old Message mechanism.

An overview of the

In development, you’re dealing with a Handler, and we’re dealing with a Handler to update the UI.

In fact, more specifically, handlers are used to switch threads.

For example, we often carry out time-consuming operations such as “network request, file access and database operation” in the sub-thread. After the operation is completed, we switch to the main thread through Handler to make some changes to the UI. It is also possible to create a child thread within a child thread, and Handler is used to switch between a child thread and its child thread.

So the Android messaging mechanism is basically how the Handler works.

The Handler’s most important job is to send and receive messages; The Handler is supported by MessageQueue and Looper. Looper runs in the thread that creates the Handler, and thread-scoped data access requires ThreadLocal, a data store class.

These blue classes are the ones we’ll focus on when discussing Android messaging, and let’s take a look at their UML class diagram.

Here’s a picture of how the messaging mechanism works to get a general idea.

The workflow of the messaging mechanism is divided into three steps:

1. Handler calls sendMessage() to queue Message into MessageQueue;

The loop() method of the Looper class takes an infinite loop from MessageQueue and passes it back to Handler.

3. Handler calls dispatchMessage() to distribute processing messages.

Let’s start with Message to see how the messaging mechanism works.

Message

This is the Message that the “Message mechanism” will deliver.

In its class description, the official sentence is:

While the constructor of Message is public, the best way to get one of these is to call Message.obtain() or one of the Handler.obtainMessage() methods, which will pull them from a pool of recycled objects.

A brief translation:

When you need to obtain a Message object, it is not recommended to instantiate it through constructors, even though constructors are public. Message.obtain() or handler.obtainMessage () are recommended. They pull a message from the message pool for reuse.

Message to message pools, which are the equivalent of thread to thread pools, reuse already processed messages from message pools rather than directly through the constructor new new messages.

When the message pool is empty, the system automatically returns a new message to the caller.

Also, the maximum size of the message pool is 50.

The Message class has an attribute int FLAG_IN_USE:

    /** If set message is in use. * This flag is set when the message is enqueued and remains set while it * is delivered and afterwards when it is recycled. The flag is only cleared * when a new message is created or obtained since that is the only time that * applications are allowed to modify the contents of the message. * * It is an error to attempt to enqueue or recycle a message that is already in use. */
    /*package*/ static final int FLAG_IN_USE = 1 << 0;
Copy the code

This property is used to indicate whether the message is in use.

When a message is queued or collected, FLAG_IN_USE is set to 1, indicating that the message is in use. If mHandler.sendMessage(MSG) is called, This message is already in use.

The Message class has a property, Message Next, that is associated with MessageQueue.

MessageQueue

MessageQueue manages a Message queue to which handlers add messages and Looper fetches them.

Although MessageQueue is called a MessageQueue, its internal data structure is a singly linked list, that is, a queue implemented with a singly linked list, as shown in the figure below.

The property Message mMessages in MessageQueue points to the first Message node M1; Each Message node, in turn, holds a reference to the next Message node through the above Message Next attribute. This is the message queue for a singly linked list implementation.

MessageQueue has several methods to focus on.

EnqueueMessage () joins the queue, which is called by Handler and adds new messages to the end of the queue.

2. Next () out of the queue, called by Looper, pulls messages from the head of the queue for immediate processing. The next() method blocks when there are no messages in the queue.

3, quit(Boolean safe) Queue exits, called by Looper. From the participation can obviously see, queue exit into the safety exit and exits, its internal call respectively removeAllMessagesLocked () and removeAllFutureMessagesLocked () two methods.

Sending Message to a Handler on a dead Thread.

4. RemoveAllMessagesLocked () unlocks all messages from the queue.

5, removeAllFutureMessagesLocked () safe exit queue, traverse the message queue, when find the first delay news, all of the messages with the subsequent recovery.

So what is a delayed message?

For example, a message added to the message queue via mHandler.postDelayed(Runnable r, long delayMillis) with a delay set for the processing of the message is a delayed message.

It can be found that we generally do not operate MessageQueue directly, even the instantiation of MessageQueue is completed in the construction of Looper.

Before analyzing Looper, let’s take a look at ThreadLocal to help us understand Looper better.

ThreadLocal

ThreadLocal is an internal data store class that can store data in a specified thread. Once the data is stored, only the specified thread can retrieve the stored data. Other threads cannot retrieve the data. — Exploring the Art of Android Development

ThreadLocal stores data for a thread that can be accessed and retrieved only by the corresponding thread.

Looper Looper is responsible for message scheduling, that is, taking messages from MessageQueue and handing them to Handler for processing.

A thread can only have one Looper. Create a Looper with looper.prepare (), and then call looper.loop () to start a loop to fetch a Message from MessageQueue.

Can’t create Handler inside thread that has not been called Looper. Prepare ()

In Looper’s class description, an example of code is provided:

    class LooperThread extends Thread {
        public Handler mHandler;
  
        public void run(a) {
            Looper.prepare();
  
            mHandler = new Handler() {
                public void handleMessage(Message msg) {
                    // process incoming messages here}}; Looper.loop(); }}Copy the code

We might have questions like:

The child thread we created did not call looper.prepare () and looper.loop () most of the time.

This is because our child threads are not using handlers. The Handler needs a Looper. The Handler needs a Looper.

In the main UI thread, we used Handler, but we didn’t call looper.prepare () and looper.loop ().

This is because looper.prepare () and looper.loop () have already been called for us in the main() function. The main() function is in the ActivityThread class.

Two other important Looper methods, quit() and quitSafely(), are extremely simple to implement internally.

    public void quit(a) {
        mQueue.quit(false);
    }

    public void quitSafely(a) {
        mQueue.quit(true);
    }
Copy the code

Both methods exit the MessageQueue by calling MessageQueue’s exit method directly.

How does Looper’s infinite loop method loop() exit?

 public static void loop(a){
 
  / /...
  
        for (;;) {
            Message msg = queue.next(); // might block
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }
            / /...
         }
         / /...
 }
Copy the code

Quite simply, the loop() method exits when no message is retrieved from the message queue.

Can’t get a message from the message queue, the message queue must exit, no possible because the message queue is finished processing?

No way.

Queue.next () will block if the message queue runs out of processing, and no messages will be retrieved.

So, if a child thread uses Looper, it must call looper.quit () to exit, otherwise Queue.next () will block forever, and neither the message queue nor Looper will be recycled, causing a memory leak.

Handler

We know that the Handler can send messages through the sendXxx() and postXxx() family of methods.

PostXxx () internally wraps the input Runnable as a Message, and sendXxx() eventually calls MessageQueue’s enqueueMessage() to enqueue the Message.

For callbacks handled by Handler messages, there are three ways to override them:

The first:

    Handler mHandler = new Handler();
    mHandler.post(new Runnable() {
        @Override
        public void run(a) {}});Copy the code

The second:

    Handler mHandler = new Handler(new Handler.Callback() {
        @Override
        public boolean handleMessage(Message msg) {
            return false; }});Copy the code

The third:

    Handler mHandler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg); }};Copy the code

As we mentioned earlier, Looper’s loop() method takes the message and passes it to handler:

    msg.target.dispatchMessage(msg)
Copy the code

DispatchMessage (MSG)

    public void dispatchMessage(Message msg) {
        if(msg.callback ! =null) {
            handleCallback(msg);
        } else {
            if(mCallback ! =null) {
                if (mCallback.handleMessage(msg)) {
                    return; } } handleMessage(msg); }}Copy the code

Line 4 of this code calls the first way the above callback method is overridden;

Line 7 of this code calls the second method of overriding the above callback method;

Line 11 of this code calls the third way to override the above callback method.

Well, that’s all the Handler does. As you can see, it does two things: call the message queue to queue the message, and call the callback method to process the message.

Afterword.

After combing through the classes above, let’s take a look at the diagram below and see that the workflow of Android messaging is not complicated.