Android——Handler Detailed Explanation

1 Introduction

Handler is a set of Android messaging mechanism, mainly used for inter-thread communication.

Describe in the simplest words: handler is actually the main thread starting a sub-thread, the sub-thread runs and generates a message, Looper gets the message and passes it to the Handler, and the Handler gets the messages in the sub-thread one by one.

Binder/Socket is used for inter-process communication, whileThe Handler message mechanism is used for inter-thread communication of the same process

It can be said that as long as there is an asynchronous thread communicating with the main thread, there must be a Handler.

In a multi-threaded application scenario, the operation information that needs to update the UI in the worker thread is passed to the UI main thread, so as to realize the update processing of the UI by the worker thread, and finally realize the processing of asynchronous messages. The use of the Handler message passing mechanism is mainly for
insert image description here
multiple Ensure thread safety while threads update UI concurrently
insert image description here

2. Explanation of related concepts

Handler、Message、Message Queue、Looper

  • Message : Represents a behavior what or a series of actions Runnable, each message has a clear target Handler when it is added to the message queue

  • ThreadLocal : Thread Local Storage (TLS for short), each thread has its own private local storage area, and different threads cannot access each other's TLS area. The role of ThreadLocal is to provide a local variable TLS in the thread. This variable works during the life cycle of the thread. Each thread has its own value (thread isolation)

  • MessageQueue (implemented by both the C layer and the Java layer): Provides insertion and deletion work in the form of a queue, and its internal structure stores messages in the form of a doubly linked list

  • Looper (Both the C layer and the Java layer are implemented): Looper means loop, it is responsible for taking out messages from the message queue and then handing the messages to Handler for processing

  • Handler : The real processor of the message, with the functions of obtaining messages, sending messages, processing messages, removing messages, etc.

insert image description here

Android message mechanism:
insert image description here

  • Take the sendMessage method of Handler as an example. When a message is sent, it will be added to the message queue MessageQueue.
  • Looper is responsible for traversing the message queue and distributing the messages in the queue to the corresponding Handler for processing.
  • The message is processed in the handleMessage method of the Handler, which completes the sending and processing process of a message.

Handler diagram:
insert image description here
model of message mechanism:

  • Message : the message that needs to be delivered, and the data can be delivered;
  • MessageQueue : message queue, but its internal implementation is not a queue. In fact, it maintains the message list through a single-linked list data structure, because the single-linked list has advantages in insertion and deletion. The main function is to deliver messages to the message pool (MessageQueue.enqueueMessage) and take away messages from the message pool (MessageQueue.next);
  • Handler : message auxiliary class, the main function is to send various message events to the message pool (Handler.sendMessage) and process corresponding message events (Handler.handleMessage);
  • Looper : Continuous loop execution (Looper.loop), read messages from MessageQueue, and distribute messages to target processors according to the distribution mechanism.

Architecture of message mechanism

  • After the child thread executes the time-consuming operation, when the Handler sends a message, it will call MessageQueue.enqueueMessage to add a message to the message queue.
  • When the loop is opened through Looper.loop, it will continuously read messages from the thread pool, that is, call MessageQueue.next
  • Then call the dispatchMessage method of the target Handler (that is, the Handler that sent the message) to deliver the message, and then return to the thread where the Handler is located. The target Handler receives the message, calls the handleMessage method, receives the message, and processes the message.

3. Basic use of Handler

3.1 Create Handler

Handler allows us to send delay messages, if the user closes the Activity during the delay, then the Activity will be leaked.

This leak is because the Message will hold the Handler, and because of the characteristics of Java, the inner class will hold the outer class, so that the Activity will be held by the Handler, which will eventually lead to the Activity leak.

Solution : Define Handler as a static inner class, hold a weak reference to Activity inside, and remove all messages in time.

public class HandlerActivity extends AppCompatActivity {
    
    

    private Button bt_handler_send;

    private static class MyHandler extends Handler {
    
    

        //弱引用持有HandlerActivity , GC 回收时会被回收掉
        private WeakReference<HandlerActivity> weakReference;

        public MyHandler(HandlerActivity activity) {
    
    
            this.weakReference = new WeakReference(activity);
        }

        @Override
        public void handleMessage(Message msg) {
    
    
            HandlerActivity activity = weakReference.get();
            super.handleMessage(msg);
            if (null != activity) {
    
    
                //执行业务逻辑
                Toast.makeText(activity,"handleMessage",Toast.LENGTH_SHORT).show();
            }
        }
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
    
    
        super.onCreate(savedInstanceState);
        setContentView(R.layout.HandlerActivity);

        //创建 Handler
        final MyHandler handler = new MyHandler(this);

        bt_handler_send = findViewById(R.id.bt_handler_send);
        bt_handler_send.setOnClickListener(new View.OnClickListener() {
    
    
            @Override
            public void onClick(View v) {
    
    
                new Thread(new Runnable() {
    
    
                    @Override
                    public void run() {
    
    
                        //使用 handler 发送空消息
                        handler.sendEmptyMessage(0);

                    }
                }).start();
            }
        });
    }
    
    @Override
    protected void onDestroy() {
    
    
        //移除所有回调及消息
        myHandler.removeCallbacksAndMessages(null);
        super.onDestroy();
    }
}

3.2 Message acquisition

There are probably several ways to obtain Message:

Message message = myHandler.obtainMessage(); 		   //通过 Handler 实例获取
Message message1 = Message.obtain();   			      //通过 Message 获取
Message message2 = new Message();      				 //直接创建新的 Message 实例

By viewing the source code, we can see that the obtainMessage() method of Handler also calls the obtain() method of Message

public final Message obtainMessage()
{
    
    
    return Message.obtain(this);
}

By looking at the obtain method of Message

public static Message obtain(Handler h) {
    
    
        //调用下面的方法获取 Message
        Message m = obtain();
        //将当前 Handler 指定给 message 的 target ,用来区分是哪个 Handler 的消息
        m.target = h;

        return m;
    }
    
//从消息池中拿取 Message,如果有则返回,否则创建新的 Message
public static Message obtain() {
    
    
        synchronized (sPoolSync) {
    
    
            if (sPool != null) {
    
    
                Message m = sPool;
                sPool = m.next;
                m.next = null;
                m.flags = 0; // clear in-use flag
                sPoolSize--;
                return m;
            }
        }
        return new Message();
    }

In order to save overhead, we try to reuse Message when using it, and use the first two methods to create it.

3.3 Handler sends messages

Handler provides a series of methods for us to send messages, such as send() series post() series, the post method needs to pass in a Runnalbe object, let's take a look at the source code of the post method

    public final boolean post(Runnable r)
    {
    
    
       return  sendMessageDelayed(getPostMessage(r), 0);
    }

But no matter what method we call, we will eventually go to the MessageQueue.enqueueMessage(Message,long) method.
Take the sendEmptyMessage(int) method as an example:

//Handler
sendEmptyMessage(int)
  -> sendEmptyMessageDelayed(int,int)
    -> sendMessageAtTime(Message,long)
      -> enqueueMessage(MessageQueue,Message,long)
  			-> queue.enqueueMessage(Message, long);

It can be found that MessageQueue is a message queue, which is responsible for entering and exiting messages.

4. Two instances (main thread - child thread)

4.1 Sub thread to main thread

First, we add a static inner class to MainActivity and override its handleMessage method.

private static class MyHandler extends Handler {
    
    
        private final WeakReference<MainActivity> mTarget;

        public MyHandler(MainActivity activity) {
    
    
            mTarget = new WeakReference<MainActivity>(activity);
        }

        @Override
        public void handleMessage(@NonNull Message msg) {
    
    
            super.handleMessage(msg);
            HandlerActivity activity = weakReference.get();
            super.handleMessage(msg);
            if (null != activity) {
    
    
                //执行业务逻辑
                if (msg.what == 0) {
    
    
                	Log.e("myhandler", "change textview");
                	MainActivity ma = mTarget.get();
	                ma.textView.setText("hahah");
            	}
                Toast.makeText(activity,"handleMessage",Toast.LENGTH_SHORT).show();
            }
        
        }
 }

Then create a private property of type MyHandler:

  private Handler handler1 = new MyHandler(this);

Finally, create a thread in the onCreate callback to receive and send messages:

new Thread(new Runnable() {
    
    
            @Override
            public void run() {
    
    
                handler1.sendEmptyMessage(0);
            }
        }).start();

in conclusion:

  • Handler subclass objects are generally created in the main thread so that they can be accessed in both threads. We created a subclass MyHandler of the Handler class, and rewritten the handlerMessage method, which is used to receive and process sent messages. Then we created a child thread, and in the child thread we used the MyHandler object to call the sendEmptyMessage method to send an empty Message. Then we can receive this data in the main thread.

4.2 Main thread to child thread

First create a MyHandler class.

private static class MyHandler extends Handler {
    
    

        @Override
        public void handleMessage(@NonNull Message msg) {
    
    
            super.handleMessage(msg);
            if (msg.what == 0) {
    
    
                Log.e("child thread", "receive msg from main thread");
            }
        }
    }

Declare a private variable of Handler type and initialize it to null by default.

 private Handler handler1;

Create a child thread and make handler point to the newly created MyHandler object.

new Thread(new Runnable() {
    
    
            @Override
            public void run() {
    
    
                Looper.prepare();
                handler1 = new MyHandler();
                Looper.loop();
                Log.e("child thread", "child thread end");
            }
        }).start();

Send messages to child threads in the main thread

while (handler1 == null) {
    
    

        }

        handler1.sendEmptyMessage(0);
        handler1.getLooper().quitSafely();

5. Principle of Handler mechanism

5.1 Overview of Handler principle

Ordinary threads do not have a looper. If you need a looper object, you must call the Looper.prepare method first, and a thread can only have one looper

How does Handler complete cross-thread communication?

  • Android uses Linux'spipe communication
    • Regarding pipelines, in simple terms, a pipeline is a file
    • At both ends of the pipeline, there are two open file descriptors. These two open file descriptors correspond to the same file, one of which is used for reading, and the other is used for writing.
  • When the message queue is created
    • Call the JNI function to initialize the NativeMessageQueue object. NativeMessageQueue will initialize the Looper object
    • The function of Looper is that when there is no message in the message queue of the Java layer, the main thread of the Android application program enters a waiting state, and when a new message arrives in the message queue of the Java layer, it wakes up the main thread of the Android application program. to process the message

insert image description here

  • Handler sends Message to MessageQueue queue through sendMessage()
  • Looper continuously extracts the Message that meets the trigger condition through loop(), and hands the Message to the target for processing
  • After dispatchMessage(), hand it back to Handler's handleMessage() for corresponding processing
  • When adding a Message to the MessageQueue, writing characters to the pipeline may wake up the loop thread; if there is no Message in the MessageQueue and it is in the Idle state, the method in the IdelHandler interface will be executed, which is often used to do some cleaning work

5.2 Handler and pipeline communication

The essence of a pipeline is also a file, but it is different from an ordinary file: the pipeline buffer size is generally 1 page, that is, 4K bytes. The pipeline is divided into a reading end and a writing end. The reading end is responsible for getting data from the pipeline, and blocks when the data is empty; the writing end writes data to the pipeline, and blocks when the pipeline buffer is full.

  • In the Handler mechanism, the Looper.loop method will continuously process the Message in a loop, and the message is obtained through the Message msg = queue.next(); method to obtain the next message. This method will call the nativePollOnce() method, which is a native method, and then enter the Native layer through JNI calls, and the pipeline mechanism is adopted in the code of the Native layer.

  • We know that memory is shared between threads. Through Handler communication, the content of the message pool does not need to be copied from one thread to another, because the memory that two threads can use is the same area, and both have the right to directly access it. Of course, there are threads Private area ThreadLocal (not involved here). Since there is no need to copy memory, what is the role of the pipeline?

  • The function of the pipeline in the Handler mechanism is that when a thread A prepares a Message and puts it into the message pool, it needs to notify another thread B to process the message. Thread A writes data 1 to the write end of the pipeline (for the old Android version, it writes characters W), and if there is data in the pipeline, it will wake up thread B to process the message. The main job of the pipeline is to notify another thread, which is the core function.
    insert image description here

Why use pipeline instead of Binder?

  • From the perspective of memory: Binder also involves a memory copy during the communication process. The Message in the handler mechanism does not need to be copied at all, and it is in the same memory itself. All the Handler needs is to tell another thread that the data is available.
  • From the CPU point of view, why does the underlying driver need a binder thread pool for Binder communication, and each communication involves the creation of binder threads and memory allocation, which wastes CPU resources.

5.3 Detailed explanation of Handler example

Handler is the upper interface of the Android message mechanism. The use process of Handler is very simple, through which a task can be easily switched to the thread where Handler is located for execution. Usually, the usage scenario of Handler is to update the UI

The following is a simple example of using the message mechanism:

public class Activity extends android.app.Activity {
    
    
	private Handler mHandler = new Handler(){
    
    
		@Override
		public void handleMessage(Message msg) {
    
    
			super.handleMessage(msg);
			System.out.println(msg.what);
		}
	};
	@Override
	public void onCreate(Bundle savedInstanceState, PersistableBundle persistentState) {
    
    
		super.onCreate(savedInstanceState, persistentState);
		setContentView(R.layout.activity_main);
		new Thread(new Runnable() {
    
    
			@Override
			public void run() {
    
    
				...............耗时操作
				Message message = Message.obtain();
				message.what = 1;
				mHandler.sendMessage(message);
			}
		}).start();
	}
}

Another example: how to send a message from the main thread to the child thread? (Although such application scenarios are rare)

Thread thread = new Thread(){
    
    
            @Override
            public void run() {
    
    
                super.run();
                //初始化Looper,一定要写在Handler初始化之前
                Looper.prepare();
                //在子线程内部初始化handler即可,发送消息的代码可在主线程任意地方发送
                handler=new Handler(){
    
    
                    @Override
                    public void handleMessage(Message msg) {
    
    
                        super.handleMessage(msg);
                      //所有的事情处理完成后要退出looper,即终止Looper循环
                        //这两个方法都可以,有关这两个方法的区别自行寻找答案
                        handler.getLooper().quit();
                        handler.getLooper().quitSafely();
                    }
                };
              
                //启动Looper循环,否则Handler无法收到消息
                Looper.loop();
            }
        };
        thread.start();
    //在主线程中发送消息
    handler.sendMessage();

Let me explain the first line of codeLooper.prepare();, first look at the Handler construction method

//空参的构造方法,这个方法调用了两个参数的构造方法
  public Handler() {
    
    
        this(null, false);
    }

//两个参数的构造方法
public Handler(Callback callback, boolean async) {
    
    
        mLooper = Looper.myLooper();
        if (mLooper == null) {
    
    
            throw new RuntimeException(
                "Can't create handler inside thread that has not called Looper.prepare()");
        }
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }
  • The Looper will be verified in the constructor of the Handler. If the Looper is empty, a null pointer exception will be thrown
  • Handler also did one more thing in the construction method, pointing one of its global message queue objects (mQueue) to the message queue in Looper, that is, this line of code in the construction methodmQueue = mLooper.mQueue;

The second line of code initializes Hanlder and overrides the HandleMessage() method, nothing to say.

Then the Looper.loop() method is called, which will be explained later.

Let's take a look at the last line of codehandler.sendMessage(message)The main role of:

First look at the code execution process after this line of code: After
insert image description here
sendMessage(), the code passes through several methods shown in the figure, and finally executes to the enqueueMessage() method of MessageQueue. Let's take a look at it:

boolean enqueueMessage(Message msg, long when) {
    
    
        if (msg.target == null) {
    
    
            throw new IllegalArgumentException("Message must have a target.");
        }
        synchronized (this) {
    
    
            msg.markInUse();
            msg.when = when;
            Message p = mMessages;
            boolean needWake;
            if (p == null || when == 0 || when < p.when) {
    
    
                // New head, wake up the event queue if blocked.
                msg.next = p;
                mMessages = msg;
                needWake = mBlocked;
            } else {
    
    
                needWake = mBlocked && p.target == null && msg.isAsynchronous();
                Message prev;
                for (;;) {
    
    
                    prev = p;
                    p = p.next;
                    if (p == null || when < p.when) {
    
    
                        break;
                    }
                    if (needWake && p.isAsynchronous()) {
    
    
                        needWake = false;
                    }
                }
                msg.next = p; // invariant: p == prev.next
                prev.next = msg;
            }
            // We can assume mPtr != 0 because mQuitting is false.
            if (needWake) {
    
    
                nativeWake(mPtr);
            }
        }
        return true;
    }
  • MessageQueue is a one-way list structure, and the main thing of MessageQueue's enqueueMessage() method is to insert the Message sent by Handler into the list.
  • When the handler.senMessage() method is called, the final result is just to insert the message into the message queue

The work of sending the message has been completed, so when did the Looper get the message, let's take a look:

public static void loop() {
    
    
        for (;;) {
    
    
            Message msg = queue.next(); // might block
            if (msg == null) {
    
    
                // No message indicates that the message queue is quitting.
                return;
            }
            try {
    
    
                msg.target.dispatchMessage(msg);
            } 
        }
    }
  • it's an endless loop
  • The purpose of this loop is to get the message from the MessageQueue
  • The method to get the message is the MessageQueue.next() method
  • After fetching the message, call the dispatchMessage() method of the message.target object to distribute the message
  • The condition for looping out is that the MessageQueue.next() method returns null

Seeing this, we should naturally think, which object is message.target. What does dispatchMessage do?

public final class Message implements Parcelable {
    
    
 /*package*/ int flags;

    /*package*/ long when;

    /*package*/ Bundle data;

    /*package*/ Handler target;

    /*package*/ Runnable callback;

    // sometimes we store linked lists of these things
    /*package*/ Message next;
}
  • After the Looper takes the Message out of the MessageQueue, it calls the dispatchMessage() method of the Handler.
    Here we can't help but ask which Handler this target points to. Let's take a look at the previous enqueueMessage
//Handler的方法
 private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
    
    
        msg.target = this;
        if (mAsynchronous) {
    
    
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }
  • The first line of code is to assign the target attribute of the message to the handler itself that sends the message
  • After the Looper takes out the message, it calls the dispatchMessage() method of the Handler that sent the message, and passes the message itself back as a parameter. At this point, the execution logic of the code returns to the Handler.

Then look at the dispatchMessage() method of the handler

/**
    *handler的方法
     * Handle system messages here.
     */
    public void dispatchMessage(Message msg) {
    
    
        if (msg.callback != null) {
    
    
            handleCallback(msg);
        } else {
    
    
            if (mCallback != null) {
    
    
                if (mCallback.handleMessage(msg)) {
    
    
                    return;
                }
            }
            handleMessage(msg);
        }
    }

Have you seen a method that we are very familiar with? —The handleMessage() method is also the logic when we process messages.

Finally, here is a working diagram of the system:
insert image description here

6. Handler usage (difference between android and java)

6.1 Refresh the UI interface

Use java:

new Thread( new Runnable() {
    
         
    public void run() {
    
         
         myView.invalidate();    
     }            
}).start();

It can realize the function and refresh the UI interface. But this is not acceptable, because it violates the single-threaded model: Android UI operations are not thread-safe and must be performed in the UI thread.

Thread+Handler:
Handler handles UI updates based on received messages. The Thread thread sends a Handler message to notify the UI to be updated.

Handler myHandler = new Handler() {
    
      
          public void handleMessage(Message msg) {
    
       
               switch (msg.what) {
    
       
                    case TestHandler.GUIUPDATEIDENTIFIER:   
                         myBounceView.invalidate();  
                         break;   
               }   
               super.handleMessage(msg);   
          }   
     }; 
class myThread implements Runnable {
    
       
          public void run() {
    
      
               while (!Thread.currentThread().isInterrupted()) {
    
        
                       
                    Message message = new Message();   
                    message.what = TestHandler.GUIUPDATEIDENTIFIER;   
                      
                    TestHandler.this.myHandler.sendMessage(message);   
                    try {
    
       
                         Thread.sleep(100);    
                    } catch (InterruptedException e) {
    
       
                         Thread.currentThread().interrupt();   
                    }   
               }   
          }   
     }   

6.2 Timer (delay operation)

Use java:
Use the TimerTask class that comes with Java. TimerTask consumes less resources than Thread. Import java.util.Timer; and import java.util.TimerTask;

public class JavaTimer extends Activity {
    
      
  
    Timer timer = new Timer();  
    TimerTask task = new TimerTask(){
    
       
        public void run() {
    
      
            setTitle("hear me?");  
        }            
    };  

    public void onCreate(Bundle savedInstanceState) {
    
      
        super.onCreate(savedInstanceState);  
        setContentView(R.layout.main);  
       
         timer.schedule(task, 10000);   // 延迟delay毫秒后,执行一次task。

    }  
}

TimerTask + Handler

public class TestTimer extends Activity {
    
      
  
    Timer timer = new Timer();  
    Handler handler = new Handler(){
    
       
        public void handleMessage(Message msg) {
    
      
            switch (msg.what) {
    
          
            case 1:      
                setTitle("hear me?");  
                break;      
            }      
            super.handleMessage(msg);  
        }  
          
    };  

    TimerTask task = new TimerTask(){
    
        
        public void run() {
    
      
            Message message = new Message();      
            message.what = 1;      
            handler.sendMessage(message);    
        }            
    };  

    public void onCreate(Bundle savedInstanceState) {
    
      
        super.onCreate(savedInstanceState);  
        setContentView(R.layout.main);  
    
        timer.schedule(task, 10000);   // 延迟delay毫秒后,执行一次task。
    }  
}  

6.3 Regularly update the UI

Runnable + Handler.postDelayed(runnable,time)

   private Handler handler = new Handler();  
  
    private Runnable myRunnable= new Runnable() {
    
        
        public void run() {
    
      
            if (run) {
    
      
                handler.postDelayed(this, 1000);  
                count++;  
            }  
            tvCounter.setText("Count: " + count);  

        }  
    }; 

and then call it elsewhere

handler.post(myRunnable);
handler.post(myRunnable,time);

6.4 Handler implements delayed execution

Handler delays 2s to execute a runnable

Handler handler=new Handler();
Runnable runnable=new Runnable() {
    
    
 @Override
	public void run() {
    
    
		// TODO Auto-generated method stub
		if(xsLayout.getVisibility()==View.VISIBLE){
    
    
			xsLayout.setVisibility(View.GONE);
		}
	}
};
handler.postDelayed(runnable, 2000);

Cancel the scheduled task before the runnable is executed

handler.removeCallbacks(runnable);

7. Summary

7.1 Summary

Behind the Handler is the assistance of Looper and MessageQueue. The three work together and have a clear division of labor.

  • Looper : Responsible for the distribution of associated threads and messages. Get Message from MessageQueue under this thread and distribute it to Handler;
  • MessageQueue : It is a queue, responsible for the storage and management of messages, and responsible for managing the Message sent by the Handler;
  • Handler : Responsible for sending and processing messages, facing developers, providing API, and hiding the details of the implementation behind it.

Messages sent by Handler are stored and managed by MessageQueue, and Loopler is responsible for calling back messages to handleMessage().

The conversion of threads is completed by Looper, and the thread where handleMessage() is located is determined by the thread where the caller of Looper.loop() is located.

7.2 Attention

(1) How many Handlers does a thread have?

  • Multiple, usually we will create more than one Handler during the development process.

(2) How many Loopers does a thread have? How to guarantee?

  • Only 1 Looper
  • The construction of Looper is private and can only be constructed through its prepare() method. When Looper's prepare() method is called, the get() method in ThreadLocal will be called to check whether Looper has been set in ThreadLocalMap?
  • If there is, an exception will be thrown, indicating that each thread can only have one Looper. If not, a new Looper object will be set in ThreadLocalMap.
  • This can ensure that ThreadLocalMap and Looper correspond one-to-one, that is, a ThreadLocalMap will only correspond to one Looper. And the ThreadLocalMap here is a global variable in Thread, and there will only be one, so it can be guaranteed that there is only one Looper in a Thread.

(3) What is the reason for Handler memory leak?

  • Inner classes hold external references.
  • Handler principle: Since the Handler can send delayed messages, in order to ensure that the message is received by the same Handler after the message is executed, the sent Message will hold a reference to the Handler. This reference is stored in the target field of the Message and is owned by the Handler. The sendMessage() method will call enqueueMessage() at the end, and in enqueueMessage(), the target field of Message will be assigned this.
  • Therefore, the Message holds a reference to the Handler, and the Handler holds a reference to the Activity, so if the Activity is destroyed before the Message is processed, a memory leak will occur.
  • How to deal with it? You can use static to modify the Handler object.

(4) Why can the main thread new Handler? If you want to prepare for the new Handler in the child thread?

  • Because the main() in the ActivityThread has already performed the prepare() operation on the Looper, you can directly create a new Handler in the main thread.
    • In the SystemServer class of android-28:
    • The main method is the entry point of the entire android application. Calling Looper.prepare() in the child thread is to create a Looper object and store the object in the ThreadLocal of the current thread. Each thread will have a ThreadLocal, which is for each The thread provides a local copy variable mechanism to achieve isolation from other threads, and this variable only works within the life cycle of the thread, which can reduce the complexity of public variable transfer between multiple methods in the same thread . The Looper.loop() method is to take out the message in the message queue and send the message to the specified handler through the msg.target.dispatchMassage() method
    /**
     * The main entry point from zygote.
     */
    public static void main(String[] args) {
    
    
        new SystemServer().run();
    }


    private void run() {
    
    
        ......
        Looper.prepareMainLooper();
        ......
    }

  • If you want to create a new Handler in the child thread, you need to manually call the Looper's prepare() method to initialize the Looper, and then call the Looper's loop() method to make the Looper run.
      new Thread(new Runnable() {
    
    
            @Override
            public void run() {
    
    
                Looper.prepare();			//初始化Looper
                new Handler(){
    
    
                    @Override
                    public void handleMessage(Message msg) {
    
    
                        super.handleMessage(msg);
                    }
                };
                Looper.loop();
            }
        })

(5) What is the processing plan for the Looper maintained in the sub-thread and when there is no message in the message queue?

  • If it is not processed, the thread will be blocked. The solution is to call Looper's quitSafely();
  • quitSafely() will call the quit() method of MessageQueue, clear all Messages, and call the nativeWake() method to wake up the previously blocked nativePollOnce(), so that the for loop in the method next() method continues to execute, and then it is found that the Message is After null, the loop will end and the Looper will end. This frees memory and threads.

(6) How to ensure thread safety internally?

  • There can be multiple Handlers to add data to MessageQueue (each Handler may be in a different thread when sending a message).
    The method of adding messages enqueueMessage() has synchronize modification, and the method of fetching messages next() also has synchronize modification.
  • Is the delay message (delayed message) time of the Handler accurate?
    Due to the locking operation described above, the time cannot be guaranteed to be completely accurate.

(7) How to create it when using Message?

  • Use the obtain() method of Message to create, and directly new out will easily cause memory jitter.
  • Memory jitter is caused by frequent new objects and frequent recycling by gc, and because it may be held by other places and cannot be recycled in time, it will lead to higher and higher memory usage.
  • Using obtain() to multiplex memory can avoid the occurrence of memory jitter. It maintains a Message pool inside, which is a linked list structure. When obtain() is called, the Message in the header will be reused, and then it will point to the next one. If there is no reusable message in the header, a new object will be created. The maximum length of this object pool is 50.

(8) What will happen to the message queue after using Handler's postDelay?

  • If the message queue is empty at this time, it will not be executed, and the waiting time for the message will be calculated, and the execution will continue when the waiting time is up.

(9) Why does the Looper infinite loop not cause the application to freeze?

  • Stuck is ANR, there are two reasons:
    • 1. There is no response to input events (such as buttons, touches, etc.) within 5s,
    • 2. The BroadcastReceiver did not finish executing within 10s.
  • In fact, all our Activities and Services run in the loop() function and exist in the form of messages, so when no messages are generated, the looper will be blocked (blocked), and the main thread will go to sleep. Once there is an input event Or the main thread will be woken up after the Looper adds the message to respond to the event, so it will not cause ANR
  • Simply put, the blockage of the looper indicates that there is no event input, and ANR is caused by an event that does not respond, so the endless loop of the looper will not cause the application to freeze.

6.3 Next

Next, let's look at HandlerThread

Guess you like

Origin blog.csdn.net/ly0724ok/article/details/117324053