Android thread communication handler

Android thread communication handler

This article discusses the Android handler mechanism.

I believe that those who have written Android must be very familiar with handlers. Because they are used so frequently. Especially when you want to refresh UI controls in non-UI threads. Because UI controls can only be refreshed in the main thread, but we may need to update the UI in non-UI threads. For example, if a background thread downloads a picture and needs to update it to the UI, the main thread handler is needed to send an update message.

Handlers are used so frequently that it is necessary for us to know how they work internally.

  • One sentence summary
  • handler thread
  • handler
    • What to send
    • Triggered thread
    • Creating a handler
    • Runnable Encapsulation
    • How to process messages
  • message
    • How to generate messages
    • Sending time
  • Looper
    • Creating a looper
    • Dispatching Messages
    • example
  • Summarize

One sentence summary

What work can the combination of handler, looper, and message do? Simply put, in one sentence: in one thread, specify to execute a task in another thread.

handler thread

What is a handler thread? When a thread creates a looper, which has a message queue and creates a handler, then this thread is a handler thread.

The function of the handler thread is to let other threads specify the handler thread to perform a task. For example, the UI thread is a handler thread. We can specify the UI thread to update the UI in a normal thread.

handler

The handler has two jobs: one is to send tasks or messages; the other is to process messages or execute tasks.

What to send

What can a handler send?

The handler and the message queue are closely related. Intuitively, the handler will send messages to the message queue. In fact, it is not only that, the handler can send both messages and runnbales. In other words, the message queue is not only a queue for messages (actually a single linked list), but also can hold runnables.

Triggered thread

The handler sends messages or tasks, usually in other threads, that is, the thread where the message is sent is not the thread that creates the handler (of course, you can also send messages in the thread that creates the handler, which is equivalent to sending messages to yourself).

The handler processes messages or performs tasks in its own thread.

Creating a handler

Handlers and loopers are not unique to UI threads. Any ordinary thread can create its own looper and handler.

But one thing to note is that before creating a handler, you must first create a looper.

If you don't create a looper, just create a new handler, for example

  1. new Thread(new Runnable(){
  2. public void run() {
  3. Handler handler = new Handler();
  4. }
  5. }).start();

When running, an error will be reported directly:

  1. Can't create handler inside thread that has not called Looper. prepare ()

Let's take a look at the handler's constructor

  1. public Handler(Callback callback, boolean async) {
  2. ...
  3. mLooper = Looper.myLooper(); // Looper.myLooper is used to get the looper of the current thread
  4. if (mLooper == null ) {
  5. throw new RuntimeException(
  6. "Can't create handler inside thread that has not called Looper.prepare()" );
  7. }
  8. mQueue = mLooper.mQueue;
  9. ...
  10. }

The handler sends the message to the message queue, so when constructing a handler, you must know the message queue to determine where to send the message.

The message queue is managed by the looper, so in order, the looper must be created before the handler can be created.

Create a thread's Looper,

  1. Looper.prepare () ;

So, the correct way to create a handler is:

  1. new Thread(new Runnable(){
  2. public void run() {
  3. Looper.prepare ();
  4. Handler handler = new Handler();
  5. }
  6. }).start();

Some students may find it strange that when using new Handler(), it can be used without calling Looper.prepare() first? That is because the handler is created in the main thread.

  1. // ActivityThread.java
  2. public   static void main(String[] args) {
  3. ...
  4. Looper.prepareMainLooper();
  5. ...
  6. }

When the main thread starts, it will call Looper.prepareMainLooper() to create a looper, so we can create a handler directly in the main thread without manually creating a looper first.

Runnable Encapsulation

You may find it strange that the message queue should contain messages, so where did the runnable go in handler.post(runnable)?

The runnable is actually also sent to the message queue, but the runnable is encapsulated before sending.

  1. public final boolean post(Runnable r){
  2. return sendMessageDelayed(getPostMessage(r), 0);
  3. }
  4.  
  5. private static Message getPostMessage(Runnable r) {
  6. Message m = Message.obtain();
  7. m.callback = r;
  8. return m;
  9. }

Use getPostMessage to wrap the runnable into a message, and the callback of the message is the runnable. Therefore, to determine whether a message is a runnable, you only need to see whether the callback of the message is empty. If it is empty, it is a normal message, otherwise, it is a runnable.

How to process messages

Look at dispathMessage

  1. public void dispatchMessage(Message msg) {
  2. if (msg.callback != null ) {
  3. handleCallback(msg); // handler.post(runnable) goes here
  4. } else {
  5. if (mCallback != null ) { // Go here when handler = new Handler(callback)
  6. if (mCallback.handleMessage(msg)) {
  7. return ;
  8. }
  9. }
  10. handleMessage(msg); // Go here when handler = new Handler()
  11. }
  12. }

According to the type of message sent by the handler, there are two cases:

  • The handler sends a message to the message queue
  • The handler sends a runnbale to the message queue

As mentioned earlier, message.callback is actually a wrapper around runnable, so if the handler sends a runnable to the message queue, the runnable will be executed.

If the handler sends a message to the message queue, it can be divided into two cases:

  • The callback is set when the handler is created, i.e. handler = new Handler(callback);
  • The callback is not set when the handler is created, i.e. handler = new Handler();

If a callback is set, the message will be processed by the callback first.

If callback returns true, it means the processing is complete and will not be passed to handler.handleMessage.

If callback returns false, it means the processing is not complete, and the message will be passed to handler.handleMessage for further processing.

If callback is not set, the message will be passed directly to handler.handleMessage for processing.

message

How to generate messages

How messages are generated

Messages can be generated by constructors. More often, they are directly obtained from the recyclable message object pool to improve performance. The way to obtain a message from the message object pool is Message.obtain(), or Handler.obtainMessage().

In addition, you can send messages without generating them. What does it mean? handler.sendEmptyMessage() can send an empty message to the message queue without constructing a message object.

Sending time

When is the message sent?

The timing of message processing is still determined by the handler (it feels like the handler has too many controls ==).

handler.sendMessage puts the message at the end of the message queue, and the looper takes messages one by one from the front to the back. handler.sendMessageAtFrontOfQueue puts the message at the head of the message queue, and the message can be processed immediately.

handler.sendMessageAtTime does not send the message to the message queue immediately, but sends it at a specified time.

handler.sendMessageDelayed does not send the message to the message queue immediately, but sends it after a specified delay.

Looper

Creating a looper

As mentioned earlier, the looper is like a transmitter, which takes messages from the message queue and dispatches them to the handler for processing. Therefore, to know which handler the message should be sent to, you must first create a looper.

How to create a looper

  1. Loop.prepare () ;

Dispatching Messages

Through looper.loop(), the looper will continuously take messages from the message queue and send them out.

How does the looper know which handler the message should be sent to?

Let's take a look at the loop method

  1. public   static void loop() {
  2. ...
  3. for (;;) {
  4. Message msg = queue. next (); // might block
  5. if (msg == null ) {
  6. // No message indicates that the message queue is quitting.
  7. return ;
  8. }
  9. ...
  10. msg.target.dispatchMessage(msg);
  11. ...
  12. }
  13. }

Each msg has a target attribute, which is the handler that sends the message. Dispatching a message means dispatching it to the msg.target object.

The loop method is not an infinite loop. Once the message queue is empty, it will end to avoid occupying CPU resources for a long time.

example

In the figure below, thread A is a handler thread. Now thread B wants thread A to process a message 5. Therefore, thread B gets the handler reference of thread A and calls sendMessage of the handler.

Message 5 is sent to thread A's message queue.

How does thread A process this message? Using the looper.loop method, it will take out a message from the message queue each time. When message 5 is taken, it means that message 5 is about to be processed.

The actual message processing logic is customized in the handler's handleMessage (or runnable, callback, here handleMessage is used as an example).

The looper gets message 5, knows the target handler through the target attribute of message 5, and then sends the message to the handler for processing.

Summarize

A handler does not exist independently. A handler must have a dedicated thread, a message queue, and a looper associated with it.

How do these roles work together? It can be simply summarized into the following four steps:

  1. The handler sends a message to the message queue. This message may be a message or a runnable.
  2. The looper is responsible for getting messages from the message queue
  3. The looper dispatches the message to the handler
  4. The handler processes the message (handleMessage or execute runnable)

The relationship between handler and looper is somewhat similar to that between producer and consumer. The handler is the producer, producing messages and adding them to the message queue; the looper is the consumer, taking messages from the message queue.

<<:  What is the connection between neural networks and mathematics? You will understand after reading this

>>:  Tech Neo July Issue: Technology Entrepreneurship

Recommend

Transaction Gold Link Apollo Mobile Terminal Abnormal Monitoring System

Overview 01Business Background and Purpose Genera...

Safety of GM's 'Super Cruise' semi-autonomous driving system questioned

According to Reuters, the U.S. Highway Traffic Sa...

How to review activity operations!

1. What is a review? Review, this word comes from...

The current status of short videos on Tik Tok and a guide to advertising

1. Current status of Tik Tok short videos In toda...

DOU+ tips and common reasons for audit failure

As a low-cost payment tool, DOU+ has always been ...

Touch: The multiple meanings of moss

Moss-covered trees in Devon, England. © Adam Burt...

How to build an event operation and promotion framework in 4 steps?

We often see various commercial activities: Tmall...

How does WeChat Mini Program obtain data?

Q: How does WeChat Mini Program obtain data? A: G...

Analysis of practical cases of information flow in 2019!

Information flow advertising is called performanc...