Interpretation of Android API 31 Handler mechanism source code

Posted by sryder on Thu, 20 Jan 2022 21:43:09 +0100

The Handler mechanism is easy for beginners to get confused, but in fact, the content of the source code related to Handler is relatively small and easy to understand. It is still very suitable as the entry point of the source code journey.

To discuss the topic of handler, it is inevitable to mention a question first. What is handler? What is Handler, there are more information on the Internet, and the old fellow can read it. Let's see what the official says today. We open the source code of android API 31} handler class, and you can see the following comments at the top,

The comments in the source code are worth reading carefully. After all, nothing is more persuasive than this,

A Handler allows you to send and process {@link Message} and Runnable objects associated with a thread's {@link MessageQueue}.

Let's start with what Hanlder can do. The Handler can do two things: send and process. It can send and process two things, Message and Runnable. It can send a lot of messages and Runnable. Processing takes time (asynchronous) and can't be processed in time. In order to deal with this situation, it needs to be managed with a queue, It is the above-mentioned MessageQueue (it will be learned from the later analysis that a thread can only have one Looper, and the MessageQueue is in this Looper, so the comment says a thread's MessageQueue is because of this relationship).

Each Handler instance is associated with a single thread and that thread's message queue.

A Handler can only be associated with one thread. As mentioned above, there is a MessageQueue in the thread.

When you create a new Handler it is bound to a {@link Looper}.

When creating a Handler, it will be bound with looper. Looper and thread are together, so it is actually bound with thread.

It will deliver messages and runnables to that Looper's message queue and execute them on that Looper's thread.

The Handler will submit the Message and Runnable to the Looper's MessageQueue, and then execute or process in the thread where the Looper is located.

There are two main uses for a Handler: (1) to schedule messages and runnables to be executed at some point in the future; and (2) to enqueue an action to be performed on a different thread than your own.

Handler has two main purposes: (1) to perform an operation at a certain time point in the future, that is, delay operation. (2) In its own thread, let another thread perform an operation.

The old fellow who reads this blog believes that the two uses have been playing 6 times as often as gray. The first is to delay the execution of an operation. The second most common is to update the UI thread from the child thread (non UI thread). Usually, the main thread and UI thread are the same. I use an instance as the starting point. The instance is the opposite of most normal situations, that is, sending messages from the main thread to the sub thread. The Demo code is as follows:,

package com.test.handlerdemo;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.util.Log;
import android.widget.Button;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity {
    private static final String TAG = "MainActivity";
    private static final int MSG_TYPE1 = 0, MSG_TYPE2 = 1;
    private Button btn_delay, btn_inter_thread;
    private TextView tv_delay_content, tv_inter_thread_content;
    private Handler threadHandler, uiHandler;
    private Thread thread;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        btn_delay = findViewById(R.id.btn_delay);
        btn_inter_thread = findViewById(R.id.btn_inter_thread);
        tv_delay_content = findViewById(R.id.tv_delay_content);
        tv_inter_thread_content = findViewById(R.id.tv_inter_thread_content);
        //Create method 1. The method is obsolete and is not recommended
        uiHandler = new Handler() {
            @Override
            public void handleMessage(@NonNull Message msg) {
                super.handleMessage(msg);
            }
        };
        /*
        //Create method 2
        uiHandler = new Handler(Looper.myLooper()) {
            @Override
            public void handleMessage(@NonNull Message msg) {
                super.handleMessage(msg);
            }
        };
        //Create method 3
        uiHandler = new Handler(Looper.getMainLooper()) {
            @Override
            public void handleMessage(@NonNull Message msg) {
                super.handleMessage(msg);
            }
        };
        */
        thread = new Thread(() -> {
            Looper.prepare();
            threadHandler = new Handler(Looper.myLooper()) {
                @Override
                public void handleMessage(@NonNull Message msg) {
                    Log.i(TAG, "threadHandler handleMessage thread id: " + Thread.currentThread().getId());
                }
            };
            Looper.loop();
        });
        thread.start();
        btn_inter_thread.setOnClickListener(v -> {
            Log.i(TAG, "sendEmptyMessage thread id: " + Thread.currentThread().getId());
            threadHandler.sendEmptyMessage(0);
        });
    }
}

Let's take a look at the creation of Handler. Here we declare two handlers: threadhandler and uihandler. The former is used to send messages to sub threads and the latter is used to send messages to main threads. From the uiHandler creation, there are 3 ways to create it, first are known by the old fellow, but Android API 31 has been labeled as not recommended. Let's look at the first one. this(null, false) is called through the parameterless construction method, that is, the following construction:

There are two main things to do here: assign values to mloop and mQueue. It can also be seen that there is only one Looper and MessageQueue corresponding to the Handler, and the MessageQueue is included in the Looper.

Takes note of the paragraph:

        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread " + Thread.currentThread()
                        + " that has not called Looper.prepare()");
        }

This code says that if mloop is empty, an exception will be thrown: you can't call loop without calling In the case of prepare(), create a handler in the xxx thread. But we didn't call looper when we actually called the new Handler Prepare(), but it doesn't throw this exception. Why? Because someone has called looper Prepare(), which is the main thread management class ActivityThread, has already prepared for us. We will talk about ActivityThread in detail later when the Activity starts. First remember that loop must be used to create handler Prepare() (only this step has been done in the UI thread, so it can be omitted).

There is no difference between the second and third constructs from the Handler level. Specify a Looper and construct it. Finally, the following construction methods are called:

Let's take a look at looper Myloop() and loop What's the difference between getmainlooper (), let's look at looper getMainLooper():

Here, the sMainLooper object is returned. Let's see when the sMainLooper object is assigned. There is only one assignment in the Looper class, that is, prepareMainLooper,

As you can see here, getMainLooper() is equivalent to myloop () in the main thread. As mentioned above, the main thread management class ActivityThread has prepared for us. Men who have seen ActivityThread must know that the prepareMainLooper is called in the main method of ActivityThread.

Well, if you can see this here patiently, old fellow is sure to be a strong and persistent man. To sum up:

1. The relationship between Handler and thread is many to one, that is, a Handler can only belong to one thread, but a thread can have many handlers.

2. Thread, Looper and MessageQueue correspond to each other one by one, and there is no one to many relationship between them.

3. Before creating a Handler, prepare the Looper of the thread to which the Handler belongs (in fact, create the Looper object).

The time is limited and the time is limited. Welcome the old fellow of iron industry to criticize and correct, discuss and exchange, and pay close attention to the follow-up.

Topics: Android