Handler Mechanism of Android View Manual

The second Handler mechanism

If you ask me what is the biggest progress in the 21st century, I think it is a convenient life. Through mobile phones, we can directly place orders for the products we want to buy, and then wait for the courier brother to deliver them to our door. We only need to sign for receipt. Rice cooker Let's get rid of the firewood in the old days, and we can remind you when the rice is cooked without the need for others to look after it. When you finish the work at hand, you can enjoy the fragrant rice directly. All in all, we strip out all kinds of things that a person needs to do. Except for things that must be done by yourself, such as eating and sleeping, you can let others or some household appliances do it.
In order to let you know that the rice is ready and the courier has been delivered, you need to be notified through some kind of occurrence. Handler plays such a role in the Android thread, because you can’t deliver the courier while setting fire at home. Cooking, so you assign all these tasks to other people. These people are multi-threaded, so how do you know their work progress or work results? For example, how long will it take for the rice to be cooked, and where has the courier been delivered? Google's solution allows you to assign a manager (Handler) to these threads, through which you can understand their situation when you need it without always paying attention.

overview

If the entire application process of Android is a company, each thread represents a department or other position, and the main thread is the company president (CEO), we can call him Mr. U (the main thread is also called UI thread), and U The general manager is naturally responsible for some important tasks, including some very important business decisions, which are relatively intuitive and generally require quick decisions. (The main thread controls the display of the UI interface and cannot perform time-consuming operations). U is always busy with affairs, so he doesn’t do everything, so he starts recruiting people (creating threads). He first recruits a secretary to work with him (at this time, the looper bound to the handler is the looper where the main thread is located), and then I recruited a few programmers to help me work (threads). Although U is always busy, I often ask my secretary to follow up on the progress. On WeChat, the secretary swipes the WeChat, and once he receives the results (MessageQueue message queue) recorded in the message from the programmer, he will sequentially send the received project name (msg.what) and project progress (msg. obj) told Mr. U that once the project is completed, Mr. U can take the project results to negotiate with the client if he has nothing else to do (interface display).
Let the main thread not do these time-consuming operations, and tell the main thread the final result in the sub-thread, which is the main purpose of Handler . If you think that there is a need for communication between threads, you can implement it through the handler. Whether it is between the main thread and the sub-thread, or between the sub-thread and the sub-thread, you can communicate through the handler. But a handler can only be bound to one thread, just like the secretary mentioned above only serves President U, because it should also divide business areas for others, just like managers of different departments, they can exchange business data and cooperation, but actually belong to different work areas.

role table

Role real identity effect Remark
U 总 The main thread is generally also called UI thread It mainly handles some Ui events and is responsible for receiving Broadcast messages. At the same time, it can also create sub-threads to handle other tasks. If the main thread handles time-consuming tasks, when the user does not process the UI interaction after more than 5 seconds, ANR will be triggered. Main thread that starts the application automatically
programmer Thread child thread Mainly responsible for processing some time-consuming work such as network requests, file reading and writing Manually started threads
little secret Handler handler Responsible for message delivery between threads, able to pass Message to message queue MessageQueue
to process messages sent through Looper
A thread can have multiple Handlers
Secretary's work report Looper circulator Loop out messages from the message queue bound to the main thread At the same time, a thread has only one looper
Xiao Mi's WeChat MessageQueue message queue Responsible for storing messages pushed by each thread -
The secretary received a WeChat message Message message The data unit of inter-thread communication, which stores the content to be communicated -

Main thread Handler business logic diagram

Combined with the description in the overview, I will describe the main work of the Handler in the main thread as follows.
insert image description here

According to this flow chart, we can divide the main work into the following steps:
1. Preparation before asynchronous thread communication
This step mainly creates Looper objects (processors), Handler objects, and MessageQueue objects (message queues) in the main thread, and These are all main thread related objects.
2. The message loop and message sending
sub-thread sends the message Message object to the MessageQueue bound to the main thread through the Handler. At the same time, the Looper will continuously cycle the message queue. Once the message is taken out from it, it will be sent to the processor that created the message (Handler ), Handler performs corresponding processing operations in the handleMessage method
3. Message processing
Handler performs corresponding processing operations in the handleMessage method. If the Handler is created on the main thread, it can perform UI operations.

Logic diagram of sub-thread Handler

Handler processing can pass messages in sub-threads to the main thread, and sub-threads can also communicate with each other through Handler, as shown in the following figure:
insert image description here

From the above figure, we need to pay attention to the following relationships:

  • 1 thread (Thread) can only be bound to 1 looper (Looper), but can have multiple handlers (Handler)
  • A circulator (Looper) can be bound to multiple handlers (Handler), and these Handlers can be of different threads
  • 1 handler (Handler) can only be bound to 1 looper (Looper)
  • The MessageQueue that the Handler sends the message depends on the Looper that was actually bound when it was created

Tips: Multiple thread Handlers can send messages to a MessageQueue held by a Looper, but the Handler that Looper wants to distribute messages is the one that originally sent the message, that is, the message sent by itself is actually processed by itself at the end. The reason can be seen below tips.

Handler uses

Let's briefly review how a Handler can be used:
1. Create Handler and message processing
By inheriting Handler and overriding the handleMessage method, the processing after message reception can be realized.
The scope of the handler instance created in it needs to include the thread that needs to send the message, otherwise Invalid for message sending.

    private  Handler handler=new Handler(){
    
    
        @Override
        public void handleMessage(Message msg) {
    
    
           super.handleMessage(msg);// 需执行的操作

        }
    };

2. Send a message from the thread

      Message message=handler.obtainMessage();
      message.what=100;
      message.arg1=i;
      message.obj="消息内容";
      handler.sendMessage(message);

where the Message object

attribute name type effect
message.what int type Can be used to identify the type of message sent
message.arg int type It can be used to transmit int-type message content, arg1 and arg2 can be assigned, and the space occupied will be smaller than that of obj
message.obj Object type Can be used to pass various types of message content

3. Use post to send and process messages
In addition to the sendMessage method, we can also send messages through post distribution, but the post method encapsulates the runner into Messgae for sending and immediate processing, and the working principle is actually similar.

    mHandler.post(new Runnable() {
    
    
            @Override
            public void run() {
    
    
                ... // 需执行的操作 
            }
    });

tips

1. How does Looper judge which Handler handles the Message?
Enter the internal source code of mHandler.sendMessage(msg) through the path below, we can find that when actually sending a message, Message.target is passed as a reference to itself as an attribute, and Looper will pass this target to the specified Handler when looping to the Message message For message distribution.

方法路径:
sendMessage(msg)
– sendMessageDelayed(Message msg, long delayMillis)
 — sendMessageAtTime(Message msg, long uptimeMillis)
   ---- enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis)

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
    
    
                 // 1. 将msg.target赋值为this
                 // 把当前的Handler实例对象作为msg的target属性
                 msg.target = this;
                 // Looper的loop()在消息循环时,会从消息队列中取出每个消息msg,然后执行msg.target.dispatchMessage(msg)去处理消息
                 // 实际上则是将该消息派发给对应的Handler实例        

                // 2. 调用消息队列的enqueueMessage()
                // 即:Handler发送的消息,最终是保存到消息队列
                return queue.enqueueMessage(msg, uptimeMillis);
 }

2. Handler's memory leak

When an object is no longer used and should be recycled, another object in use holds a reference to it, causing the object to no longer be recycled. This causes the objects that should have been recycled to stay in the heap memory, resulting in a memory leak.

Leakage reason
When the Message exists in the MessageQueue and has not been released (unprocessed message/when the message is being processed), it means that there is an instance of Handler being held by the Message. The reason for the holding is that the Message in the first knowledge point will put the current Handler The instance object can be known as the target attribute of msg.
Since Handler = non-static inner class/anonymous inner class (2 ways of use), it holds the reference of the outer class (that is, the Activity instance) by default. Once the Activity is about to be recycled, the Handler instance stays in the heap memory. Therefore, the activity cannot be recycled, which leads to memory leaks.
insert image description here

Solution
1. Use a static inner class
Principle: The static inner class does not hold a reference to the outer class by default, so that the reference relationship of "unprocessed/processing message -> Handler instance -> outer class" does not exist.
Specific solution: Set the subclass of Handler as a static inner class. In addition, WeakReference can also be used to hold external classes to ensure that the external classes can be recycled. Because: weakly referenced objects have a short life cycle. When the garbage collector thread scans, once an object with a weak reference is found, its memory will be reclaimed regardless of whether the current memory space is sufficient or not.
Solving code:

    // 设置为:静态内部类
    private static class SHandler extends Handler{
    
    

        // 定义 弱引用实例
        private WeakReference<Activity> reference;

        // 在构造方法中传入需持有的Activity实例
        public SHandler(Activity activity) {
    
    
        // 使用WeakReference弱引用持有Activity实例
         reference = new WeakReference<Activity>(activity); }

        @Override
        public void handleMessage(Message msg) {
    
    
        
        }
    }

2. Use the life cycle of the external class to clear the message queue in the Handler.
The principle: not only makes the reference relationship of "unprocessed/processing message -> Handler instance -> external class" no longer exist, but also makes the life cycle of the Handler ( That is, the period when the message exists) Synchronize with the life cycle of the external class
Specific scheme: When the external class (here, Activity is taken as an example) ends the life cycle (the system will call onDestroy() at this time), clear all messages in the Handler message queue ( Call removeCallbacksAndMessages(null))
to solve the code:

@Override
    protected void onDestroy() {
    
    
        super.onDestroy();
        mHandler.removeCallbacksAndMessages(null);
        // 外部类Activity生命周期结束时,同时清空消息队列 & 结束Handler生命周期
    }

3. Quickly switch to the main thread execution scheme
1. Switch by creating a Looper Handler bound to the main thread

    new Handler(Looper.getMainLooper()).post(new Runnable() {
    
    
        @Override
        public void run() {
    
    
            //此时已在主线程中
        }
    });

2, activity.runOnUiThread(Runnable action) method

public void updateOnUI(Activity activity) {
    
    
        activity.runOnUiThread(new Runnable() {
    
    
            @Override
            public void run() {
    
    
                //此时已在主线程中
            }
        });
    }

3、view.post(Runnable action)

public void updateOnUI(View view) {
    
    
        	view.post(new Runnable() {
    
    
        	@Override
      		public void run() {
    
    
      	      //此时已在主线程中
      	    }
    	});
	}

Possible related problems

1. What is the difference between handler.sendMessage() and handler.post()?
Looking at the source code of Handler, it can be found that the final method sent to MessageQueue is enqueueMessage() in Handler, but for the processing of Message, the result of the message will be returned to its callback when it is distributed.
2. How many Handlers can a thread have? How many loopers? How many MessageQueue?
Handler: multiple, because the target method of Message is used to mark the Handler when adding and distributing to achieve accurate distribution, so the same thread can create multiple Handler objects. Looper: one, to get the Looper, you need to call the Looper.prepare() method
first , look at the source code below and you can find that if multiple Looper objects are included, an exception will be thrown.

 private static void prepare(boolean quitAllowed) {
    
    
        if (sThreadLocal.get() != null) {
    
    
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        sThreadLocal.set(new Looper(quitAllowed));
    }

MessageQueue: One, when Looper.prepare() is called, it will judge whether the Looper of the thread is empty. Only when it is empty, the constructor of Looper will be called to create MessageQueue, so there is only one.

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

Self-introduction of interview skills

It is already a fixed process and procedure for the interviewer to introduce himself at the beginning of the interview, but we can improve our competitiveness through the following points in this regard: 1. The time for self-introduction should be controlled within 1-3 minutes, and be careful not to exceed 3 minutes
. Minute
2. Don’t just use adjectives without specific experience. Use data and results to express your own experience. For example, to express your technical ability, you can use time, technology stack, and results as keywords to describe. For example, use Google CameraX and Detect packaged a face recognition sdk in about 3 days, including the presentation of multiple faces and single faces and the View that can be used as a face recognition package.
3. Talk about your own advantages and try to get closer to the job requirements.
4. You can package yourself appropriately during the interview, but remember not to lie

Guess you like

Origin blog.csdn.net/number_cmd9/article/details/123977367