Copyright notice: this article is the blogger’s original article, without the permission of the blogger shall not be reproduced source: github.com/AnliaLee if you see any mistakes or good suggestions, welcome to leave a comment
preface
Android specifies that changes to UI controls and updates to views must be done in the UI thread (main thread). Some time-consuming operations, such as loading network data, querying local files and data, must be placed in child threads. So we need a communication mechanism that allows the child thread to tell the UI thread to update the interface when it completes its task. This chapter will take a look at the handlers of thread communication and talk about their interactions with ThreadLocal, Message, MessageQueue, and Looper
Ps: This blog is mainly a guide to help you sort out the relationship between Handler, Looper, MessageQueue and other roles, as well as their role in the Handler message mechanism, and will not be too deep into the source code. As for the source code, there are too many excellent online articles, here recommend a few predecessors write blog, we can look at each other
- Android Advanced – Android messaging mechanisms for Looper, Handler, and MessageQueue
- Android’s asynchronous Message processing mechanism gives you insight into the Looper, Handler, and Message relationships
- Android development – Android messaging mechanism details
Ps2: Read this blog to understand the source code to help digest knowledge oh ~
Android multithreading (a) Thread and Runnable (b) synchronized use parsing
Child threads send messages to the main thread
In the far reaches of the Android continent, there is a war between country U (the UI Thread, the main Thread) and country T (Thread, the child Thread). One day, U troops prepare to attack THE capital of T country R city, as long as the underground organization (Handler) agent small H (Handler instance) after the signal, can take the corresponding action (update UI). Due to the high security in R City, little H needs to be very private when communicating signals. Therefore, the following plan is made by little Hand his special Courier, Little L (Looper, Handler is associated with a Looper object when created, and Looper is stored in ThreadLocal. Each thread maintains its own Looper, which naturally belongs to the main thread.
- Little H left instructions for various specific signals before the infiltration mission, and the organization could take corresponding actions according to the instructions
When you create a Handler instance, override the handleMessage method, where you write the UI update operation, to execute after the message is allocated
public class HandlerTestActivity extends AppCompatActivity {
TextView textShow;
private static final int CODE_TEST_ONE = 101;
private static final int CODE_TEST_TWO = 102;
private static final int CODE_TEST_THREE = 103;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_handler_test);
textShow = (TextView) findViewById(R.id.text_show);
}
private Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
switch (msg.what){
case CODE_TEST_ONE:
textShow.setText("Start spying...");
break;
case CODE_TEST_TWO:
textShow.setText("Intelligence gathered...");
break;
case CODE_TEST_THREE:
textShow.setText("General attack!");
break; }}}; }Copy the code
- When h sneaks into the city, as long as the time is ripe, he will write the signal on the paper (Message) and plug it into the hole (MessageQueue) dug by L on the city wall, as shown in the picture below (Soul painting hand).
MessageQueue: The list of messages is stored in a single linked list data structure. Messages are accessed on a first-in, first-out basis. When a Looper instance is created in the thread, a MessageQueue paired with it is automatically created
When we send messages using Handler instances in child threads, Handler is called internal methods enqueueMessage insert Message to the MessageQueue (Handler. The last call MessageQueue enqueueMessage method. EnqueueMessage method to store the Message , please refer to Appendix 1 below for details about how the Handler sends messages.)
public class HandlerTestActivity extends AppCompatActivity {
// omit some code...
public void clickEvent(View view) {
switch (view.getId()) {
case R.id.btn_start:
new Thread(new TestRunnable()).start();
break; }}private class TestRunnable implements Runnable{
@Override
public void run(a) {
try {
handler.sendEmptyMessage(CODE_TEST_ONE);
// You can also send messages this way
// Message message = Message.obtain();
// message.what = CODE_TEST_ONE;
// handler.sendMessage(message);
/ / or
// message.sendToTarget();
Thread.sleep(2000);
handler.sendEmptyMessage(CODE_TEST_TWO);
Thread.sleep(2000);
handler.sendEmptyMessage(CODE_TEST_THREE);
}catch(InterruptedException e){ e.printStackTrace(); }}}}Copy the code
- The small L who is responsible for receiving outside the city will always stay by the hole (Looper. Loop), once it finds the paper (Messagequeue.next) in the hole, it will take it out and send it back to the organization (handler.dispatchMessage), and then return to continue to stay in the hole
The looper. loop method continuously calls messagequyue. next to read the message, or handler. dispatchMessage to distribute the message if it is not empty
- After the organization gets the note, it first determines whether the note is issued by little H, and then takes corresponding actions according to the signal on the note after confirming it is correct
Handler’s dispatchMessage method was called in Looper, and handler. handleMessage was called in dispatchMessage, which brings us back to the code we rewrote in point 1, Implement the operation of sending a message from the child thread to the main thread to update the UI
The final running effect is shown in the figure
Summarize the entire process of creating and sending a message to a Handler, as shown below
The main thread sends messages to child threads
The Looper for the main thread is created before the application is started. If we want to send messages to the child thread in the main thread, we need to manually create the Looper and start the loop when the child thread is created.
public class HandlerTestActivity extends AppCompatActivity {
private Handler handler2;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_handler_test);
TestThread testThread = new TestThread();
testThread.start();
while (true) {// Make sure testThread. Looper is initialized
if(testThread.looper! =null){
handler2 = new Handler(testThread.looper){
@Override
public void handleMessage(Message msg) {// The child thread executes after receiving the message
switch (msg.what){
case CODE_TEST_FOUR:
Log.e(TAG,"Received message sent by main thread");
break; }}}; handler2.sendEmptyMessage(CODE_TEST_FOUR);// Send messages in the main thread
break; }}private class TestThread extends Thread{
private Looper looper;
@Override
public void run(a) {
super.run();
Looper.prepare();// Create a Looper instance of the child thread
looper = Looper.myLooper();// Get the Looper instance of the child thread
Looper.loop();// Start the loop}}}Copy the code
Of course, the above code is only a brief experience of creating a Looper manually. In fact, the HandlerThread class is already wrapped for us. It helps us to create a Looper, start a loop, etc., so using HandlerThread is more convenient and safer. Using the same operation as above, this time we create the child thread directly by inheriting HandlerThread:
public class HandlerTestActivity extends AppCompatActivity {
private HandlerThread handlerThread;
private Handler handler2;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_handler_test);
handlerThread = new HandlerThread("MyHandlerThread");
handlerThread.start();
handler2 = new Handler(handlerThread.getLooper()){
@Override
public void handleMessage(Message msg) {// The child thread executes after receiving the message
switch (msg.what){
case CODE_TEST_FOUR:
Log.e(TAG,"Received message sent by main thread");
break; }}}; handler2.sendEmptyMessage(CODE_TEST_FOUR);// Send messages in the main thread
}
@Override
protected void onDestroy(a) {
super.onDestroy(); handlerThread.quit(); }}Copy the code
More information about HandlerThread can be found in this blog post
Android Advanced 15: HandlerThread usage scenarios and source code parsing
Child threads send messages to child threads
The child thread sends a message to the child thread
protected void onCreate(Bundle savedInstanceState) {
// omit some code...
handlerThread = new HandlerThread("MyHandlerThread");
handlerThread.start();
handler2 = new Handler(handlerThread.getLooper()){
@Override
public void handleMessage(Message msg) {// The child thread executes after receiving the message
switch (msg.what){
case CODE_TEST_FOUR:
Log.e(TAG,"Received a message from another child thread");
break; }}}; Thread testThread =new Thread(new Runnable() {
@Override
public void run(a) {
handler2.sendEmptyMessage(CODE_TEST_FOUR);// Send the message in another child thread}}); testThread.start(); }Copy the code
Appendix I: Handler sends messages
Handler sends the message a variety of methods, but no matter what method we use, the final is MessageQueue. By using enqueueMessage method insert message to the message queue. The internal execution sequence of each method is shown in the figure below. We can start from any step in the red box. We only need to pay attention to the function of the method and the difference of the parameters passed in