As the saying goes, great oaks from little acorns grow, learn math, physics and chemistry, go all over the world are not afraid; Only under the condition of laying a good foundation, can we go further to learn knowledge; Today, WE will design the Handler mechanism. We will explain it in a special way. I hope it will be helpful to you.

Handler’s puzzle:

1. First of all, I have a question. Why do interviewers like to ask Handler about relevant knowledge? Because the Handler mechanism is a very important communication mechanism for Android, many framework implementations use the Handler to update the UI;

Where does Android use the Handler messaging mechanism, and why? Let’s take a step-by-step look at what Handler really is.

Handler’s previous life:

1. In the Android world, there are countless threads, among which a special thread is given a special mission, which is the legendary main thread; Why is it special? Because it has the power to update the UI (view); So the question is, what about some other thread saying I’m going to update my UI as well? You can’t give this thread the right to update the UI. If every thread updates the UI, it’s easy to get messed up. It’s very simple, just let the main thread update it; So how do you tell the main thread to update? Keep reading;

2. After the Google engineers built the main thread, they also created some tools for the main thread to use. What tools are they? The Handler is a communication tool used to send messages ️; When another thread needs to update the UI, just call the main thread and tell it what Message you want to send. ️

3. How do communication tools work? When a communication tool sends a message, it sends it to the nearest communication base station (MessageQueue). At this time, another role is introduced, that is, a communication satellite (Looper), a hard-working worker who works all the time. The function of Looper is to fetch the message from MessageQueue and send it to the communication tool of the main thread. At this time, the main thread’s callBack receives the message from other threads and updates the UI.

Of course, the above is purely personal example, used to deepen understanding!

Let’s take a look at Handler’s design!

Handler mechanism design:

1, before we start

If you are a Google engineer, you are faced with a problem. Multiple threads are working together, and everyone has the need to update the UI. From time to time, you update the UI, and I update the UI, at this time, it may cause the updated things to be overwritten by other threads, which is the problem of multiple threads operating at the same time. So how do we solve this problem?

2. The need to update the UI

Since everyone has the need to update the UI, why not unified management, use a thread to update the UI, other threads need to update the UI, tell the thread that can update the UI, let it update the line, so that can avoid the above situation; Now that I have the idea, how do I implement it? Keep reading!

3. Demarcating thread boundaries

First set a thread as the main thread, give it the power to update the UI, and then design a communication tool that can send messages, named it Handler, Handler’s responsibility is to send messages, notify the thread that can update the UI, we need to update the UI; So the design looks like this, with a method that can send a message, enqueueMessage();

public class Handler {
    private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        msg.target = this;
        returnqueue.enqueueMessage(msg, uptimeMillis); }}Copy the code

Now that you have a tool for sending messages, you have to design another tool for receiving them;

4. Design of MessageQueue and Message

Before designing the UI, there is a confusion that if there are multiple threads working on updating the UI at the same time, it cannot be done at the same time; For example, several people want to buy train tickets at the ticket office, but the office has only one window, so it is impossible to sell tickets to so many people at the same time. At this time, people can queue up and buy tickets in an orderly way, so that everyone can buy tickets without chaos. We can do the same thing here; MessageQueue: MessageQueue: MessageQueue: MessageQueue: MessageQueue: MessageQueue: MessageQueue

Public final class MessageQueue {Message mMessages // Method of storing messages Boolean enqueueMessage(Message MSG, long when) { synchronized (this) { Message p = mMessages;if(p == null) {// Add the first message to the queue MSG. Next = p; mMessages = msg; }else {
            Message prev;
                for(;;) { prev = p; p = p.next; } // queue the incoming message after the previous message. }}return true; }}Copy the code

MessageQueue (MessageQueue) mainly designed a linked list structure, so that incoming messages can be linked to the last message, so as to achieve the purpose of queuing, that is, enqueueMessage() method;

Next is a queued tag that marks who is next to it and is used to link queues:

public final class Message implements Parcelable { public int what; . Message next; }Copy the code

4. Looper’s design

Now that you have a Handler for sending messages and a queue for receiving messages (MessageQueue), you also need a tool to pull messages out of the MessageQueue;

Before designing, there is a question: threads may or may not send messages, I don’t know if there are messages in the message queue;

Therefore, this tool needs to constantly check whether there are messages in the message queue, if there are messages, it will be taken out, to avoid delaying the need for other threads to update the UI, which requires that this tool needs to work constantly, so it is designed as a non-stop Looper;

Round-robin designed an infinite loop mechanism, can keep pulling messages from the message queue, so the design effect is as follows:

public final class Looper {
    public static void loop() {... final Looper me = myLooper(); final MessageQueue queue = me.mQueue; // Continuously loop to fetch messages from the queue;for(;;) {... Message msg = queue.next();if (msg == null) {
                // No message indicates that the message queue is quitting.
                return; } the try {/ / callback to the main thread MSG. Target. DispatchMessage (MSG); } finally { ... }... }}}Copy the code

Looper has an infinite loop that keeps retrieving messages from the MessageQueue.

At this point, there is a question: how to send the message from Looper to the main thread?

We can call this message back to the main thread by designing a callback;

The next step is to design an interface inside the Handler that calls back Looper messages to the main thread through the handleMessage() method, which ultimately calls back through the dispatchMessage Handler.

public interface Callback {
    /**
     * @param msg A {@link android.os.Message Message} object
     * @return True if no further handling is desired
     */
    public boolean handleMessage(Message msg);
}
    
/**
 * Handle system messages here.
 */
public void dispatchMessage(Message msg) {
    if(msg.callback ! = null) { handleCallback(msg); }else {
        if(mCallback ! = null) {if (mCallback.handleMessage(msg)) {
                return; }}... }}Copy the code

The dispatchMessage() in Handler is designed to be called by Looper;

5. Design of ThreadLocal

So this is the end of a complete messaging mechanism, but is this the end of it? But things are not so simple, let me tell you!

The above design is just a set of message mechanism for the main thread, a round cycle (Looper) + queue (MessageQueue), so when other threads also need to communicate? You can’t all use the main thread messaging mechanism, that would be messy;

In this case, each thread is designed with a separate Looper + MessageQueue for its own communication; So the problem comes again, so many threads, corresponding to so many Looper + queue (MessageQueue), how to manage is also a problem; There are so many message mechanisms, which belong to their own thread, which belong to other threads, must be demarcated, otherwise, thread A wants to send messages to thread B, the result is sent to thread C, which is chaotic.

In this case, we bind threads to Looper + MessageQueue, and manage these Looper + MessageQueue by designing a manager. Each thread corresponding to each round robin (Looper) + queue (MessageQueue) well one-to-one correspondence;

Let’s create a ThreadLocal class to manage these relationships and see what happens:

public class ThreadLocal<T> {

    public void set(T value) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if(map ! = null) map.set(this, value);else
            createMap(t, value);
    }
    
    public T get() {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if(map ! = null) { ThreadLocalMap.Entry e = map.getEntry(this);if(e ! = null) { @SuppressWarnings("unchecked")
                T result = (T)e.value;
                returnresult; }}return setInitialValue(); }}Copy the code

Design two methods, through the way of key-value pair to store and retrieve, thread as the key, Looper as the value, store it;

We then set this ThreadLocal to a globally unique variable in the Looper so that other threads can fetch their Looper from ThreadLocal at any time.

The effect is as follows:

Public final class Looper {// Global unique variable, Static final ThreadLocal > sThreadLocal = new ThreadLocal<Looper>(); public static voidloop() {... final Looper me = myLooper(); final MessageQueue queue = me.mQueue; // Continuously loop to fetch messages from the queue;for(;;) {... Message msg = queue.next();if (msg == null) {
                // No message indicates that the message queue is quitting.
                return; } try { msg.target.dispatchMessage(msg); } finally { ... }... }}}Copy the code

6. Collaboration

Now that the tools for each module have been designed, the next step is to make these modules work together to assemble a complete messaging mechanism.

Handler: sends messages. MessageQueue: MessageQueue to receive messages; Looper: a round-robin device that continuously retrieves messages from a message queue; Message: Message structure, supporting linked list structure; ThreadLocal: Looper for managing threads;

Here we bind the round-robin (Looper) + queue (MessageQueue) to serve a thread; The Handler then fetches its own Looper through ThreadLocal, so that the message is sent to the MessageQueue (MessageQueue) in the Looper, which is then looped back to the corresponding thread through the Handler interface.

Let’s take a look at the structure after design:

At this point, a complete message mechanism is designed;

Let’s look at the diagram in action:

Handler design is very clever, the source code in a lot of details, I will not post out, here just talk about the Handler structure design, suggest that you can follow the source code to go again, used to deepen your understanding!

conclusion

1. Each thread has its own round-robin Looper and MessageQueue MessageQueue to receive messages from other threads. Each thread has its own unique Looper and MessageQueue. Handler can be created indefinitely, because the Handler created will be bound to the thread’s Looper. 3. After the Handler sends the message, it queues up the message in the message queue and does not receive the response immediately. 4. Messages in MessageQueue, the MessageQueue of Handler, have a lag, so there is the risk of memory leakage; 5. The Message of the Handler is a linked list structure used for queuing in the Message queue MessageQueue. ThreadLocal is used to manage the Looper of threads to ensure one-to-one pairings.

About me

Brother Dei, if my article is helpful to you, please like ️, you can also follow my Github and blog;

Welcome to communicate with me about technology;