Why write a Handler mechanism?

Writing a Handler mechanism that works is our goal, but not our goal. The most important thing is to think and understand the Handler design along the way! It’s like the best thing about travel is what you see and think. After completion, we will study the Handler mechanism provided by the Android system, which will be more helpful!

How hard is it to write your own Handler mechanism?

Before, when discussing the requirements of a scheduled task with my colleagues, we summarized several ways to implement the scheduled task respectively, and mentioned the use of Handler to handle the scheduled task. Suddenly, a question occurred to me: How can the scheduled task in Handler be timed? When mind is across the Message. The next () function to get the Message the MessageQueue. When the Message nativePollOnce () function, fuzzy remember the inside is introduced into a time parameter, not absolutely sure.

It’s not a good habit to be vague about basic concepts. Being vague about an important concept during daily development can lead to bugs that take a lot of time to fix. So why don’t we focus some of our time on getting to the bottom of the ambiguity so that we don’t have to spend a lot of time on it later?

I will follow the following steps to illustrate my understanding of the Handler message processing mechanism. The article will be divided into two parts. The first part is to design a homemade Handler processing. The second part is based on the source code analysis of the Handler mechanism and thinking about the interview encountered Hanler problems and unpopular Handler problems.

  1. Handler Handler Handler Handler Handler Handler Handler Handler Handler Handler Handler Handler Handler Handler Handler Handler Handler Handler Handler
  2. Parse the Message processing process by condensing the source code into pseudocode.
  3. Use pseudocode to detail how the components (Handler/Looper/MesaageQueue/Message) do their jobs and work together.
  4. Summarize and list some important concepts and interview questions about the Handler mechanism.

At the same time, I still recommend students who want to understand the Handler mechanism to read the Handler source code, the Handler mechanism related source code is relatively simple to read not difficult, the overall learning pace is mainly to read the source code, online articles (including this one) as a supplement, their own independent thinking to learn.

The source version used below is SDK 29!!!!

The essence of the Handler mechanism is an ordered message processor

Understand the essence, Handler is nothing mysterious

First of all, we need to understand the nature of the Handler message processing mechanism. An ordered message Handler, the purpose of which is to ensure that UI updates can only be performed by a single thread the single-thread UI update mode, so that multiple threads do not cause problems when updating the UI.

With this in mind, let’s imagine the components of an ordered message processor. If you were to design a single ordered thread handling mechanism, what would you do?

  1. Someone who sends messages.
  2. A container for storing messages, messages sent by a total container, otherwise the sent messages have no time to deal with what to do?
  3. Responsible for constantly fetching messages from the container.
  4. Responsible for processing messages.
  5. The carrier of the message itself, used to specify the format of the message, can not be any type of message sent and processed.

Finally, a pattern should emerge in which messages flow in one direction, one message at a time, and the messages in the container are ordered.

Pseudo code homemade Handler mechanism and design ideas

Based on the above design ideas, we use pseudo code and combined with the actual mechanism of Android Handler to achieve a single ordered message processing mechanism!

  1. Message carrier

    We use a Message object as a Message carrier to carry the Message, so its class pseudocode is as follows:

    public class Message {
    		public String content;// The content of the message
    		public int id;// The identification id of the message, which is easy to handle separately.
    }
    Copy the code
  2. Message containers, stored directly using the queue structure, a single order all have.

    public class MessageQueue {
        private Message mMessage;
    
        // synchronized handles thread safety
        public void addMessage(Message msg){
            if(mMessage ! =null){
                mMessage.next = msg;
            } else{ mMessage = msg; }}// Get the queue header message
        public Message next(a){
            if (mMessage == null) return null;
            Message current = mMessage;
            mMessage = current.next;
            returncurrent; }}Copy the code
  3. Get the message and process it, and keep getting it? Simple, an endless loop can be done. The other thing we need to do here is how do we get messages to be processed in the target thread, so that we can process messages in a single thread?

    public class Looper {
    		private MessageQueue messageQueue;
    		
    		public void loop(a){
    				for(;;) { Message msg = messageQueue.next(); }}}Copy the code
  4. What do you do with the messages you get? And most importantly, how do you ensure that all message processing is performed in the target thread?

    Design ideas

    First of all, the first goal, how to deal with it? This simply uses a Handler class that contains functions that handle messages. What about the second goal?

    Since Handler is the message processing class, the message processing of the instantiated object of Handler class must be located in the target thread. Secondly, Lopper needs to rely directly on MessageQueue. Just leave MessageQueue initialization to Looper, and then you just need to think about Looper’s design.

    A Looper has an infinite loop, so there can only be one Looper in a single thread (it doesn’t help to have more than one). Then think about the relationship between Hnadler and Looper. Looper gets the message and Handler handles the message. There’s nothing else to design. Exit mechanism? Add one if you want, exit with no message, or delay exit.

    Processing sending messages

    The purpose of sending a message is to add a MSG to MessageQueue. What we need to consider here is how to deal with the interaction between multiple threads. Here we use the Wait/notify mechanism for Java threads (also used in early Android systems). Looper locks the message in an infinite loop, and waits () when the message is empty. Notify () wakes up the Looper thread when a message is added to process the message.

    So far, Handler and Looper’s rework looks like this:

    // Handler 
    public class Handler {
    		/** * The method that handles Message needs to be implemented by the successor. * * /
    		public void handlerMessage(Message msg){
    			// Process different MSGS according to their ID}}// Looper after adding functionality
    public class Looper {
        private MessageQueue messageQueue;
        public Handler mHandler;// Handle messages
        
        private Object lock = new Object();// Thread lock, to avoid endless loop consumption of resources, control the sending thread and looper thread safety
    
        public Looper(a){
            messageQueue = new MessageQueue();
        }
    
        /** * Add a Handler to Looper **/
        public void setHandler(Handler handler){
            this.mHandler = handler;
        }
    
        public void addMessage(Message msg){
            synchronized (lock){
                messageQueue.addMessage(msg);
                // The message joins to wake up the loop threadlock.notify(); }}public void loop(a){
            for(;;) {synchronized (lock){
                    Message msg = messageQueue.next();
                    if(mHandler ! =null&& msg ! =null){
                        mHandler.handlerMessage(msg);
                    } else {
                        / / wait for
                        try {
                            lock.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }
    }
    Copy the code
  5. use

        public static void main(String[] args){
            / / to create stars
            Looper looper = new Looper();
            // Create Handler instances to process messages
            Handler handler = new Handler(){
                @Override
                public void handlerMessage(Message msg) {
                    System.out.println("ID:" + msg.id + " Content: "+ msg.content); }};// Set Handler to Looper
            looper.setHandler(handler);
    
            // Start the child thread and send a message every once in a while
            Thread thread = new Thread(new Runnable() {
                int id = 0;
                @Override
                public void run(a) {
                    for(;;) {// Sleep for 3 seconds at a time to control frequency and thread safety
                        try {
                            Thread.sleep(3000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        Message msg = new Message();
                        msg.id = id;
                        msg.content = "Content encoding" + id;
                        // Send a messagelooper.addMessage(msg); id++; }}}); thread.start();// Start the loop query processing message
            looper.loop();
        }
    Copy the code

    The result is perfect

summary

Now that we have completed a message handling mechanism that is similar to the Android Handler mechanism, in the next article I will analyze the Android Handler mechanism from the source code and think about the handlers-related problems encountered in interviews and some cold questions (alas, Why not ask out-of-the-way questions? . Then we’ll refine which analysis picture we started with.

Finally, learning Android system source code related issues or should be based on the source code, standing on the shoulders of giants independent thinking 🤔