I have to say Handler

          Android provides us with the handler class, which is a must-have knowledge point in Android development. This article discusses the use of handlers between the main thread and child threads, because the use between child threads and child threads is more complicated and will not be discussed. This article will summarize the use of handlers in the following order.

  • what is handler? In what situations is handler suitable for use? The role of handler?
  • What is the relationship between handler and Looper, Message, MessageQueue, and thread?
  • Several common methods of handler.
  • A small case of using handlers to pass messages.

1. What is handler? In what situations is handler suitable for use? The role of handler?

Handler is a basic component of the android framework. It is generated by android in response to development needs. It is used to solve the problem that the main thread cannot handle long-term operations and the UI cannot be updated in the child thread. If you process operations for more than about 5 seconds in the main thread, such as requesting network pictures, requesting network data, and requesting background data, the interface will pop up ANR (application not responding) crash, if the data is requested in the child thread to start updating It will also report an error and crash, because he stipulates that the main thread is dedicated to handling UI updates and user interaction. There are two functions of handler, the first is to solve the communication problem between the main thread and the sub-thread; the second is to set up timed tasks.

Second, what is the relationship between handler and Looper, Message, MessageQueue, and thread?

**************Message***************************

A message is a container that loads any data type, and a carrier of data communicated between the main thread and the child thread. Message is a class with 4 common properties.

what: A user-defined message code so that recipients can identify which Message this is. Each Handler uses its own namespace for message codes, so you don't need to worry about your Handler colliding with other handlers.

arg1\arg2: Store integer data.

object obj: Stores an object, which can be a custom class and can load many types of data.

Some methods provided by handler can create message objects.

public final Message obtainMessage()
public final Message obtainMessage(int what)
public final Message obtainMessage(int what, Object obj)
public final Message obtainMessage(int what, int arg1, int arg2)
public final Message obtainMessage(int what, int arg1, int arg2, Object obj)

 

**************************MessageQueue*******************************

messageQueue is a queue for storing messages, which is a first-in, first-out structure. When the UI thread of Android is created, a looper and messageQueue queue are built for us internally to store data, but the child thread we create by ourselves needs to manually create the looper. The main thread can only have one looper object and only one messageQueue message queue, but there can be multiple handlers. Since the thread can only process one task at a time, the messageQueue has only one message per line. After the looper is created, it will get a reference to the messageQueue, so the looper can find the messageQueue (the code encapsulated by android); and the handler will also get the looper object (also the encapsulation code) of the thread where it is created when it is created; so that the handler eventually holds the looper and messageQueue. citations, you can find them.

*****************************handler*******************************

Inside the hanlder is the task responsible for sending messages and processing the messages it sends. Because the handler has the address of the messageQueue, it can insert the message into the queue tail of the message queue messageQueue, and the message actually holds the reference of the handler, so the looper can find the handler that sent him to deal with it after taking out the message. The handler is like a secretary who shuttles back and forth between the child thread and the main thread.

**************************looper*******************************

The looper has been created after the main thread is created, and is associated with the main thread (but the sub-thread created by itself does not create the looper, so it has to be added manually). For processing, once a message is found, it will be taken out immediately. There is a reference to the handler on the message, and then it will be distributed to the handleMessage() method of the handler to process the message. After the message is processed, the next one will be taken out. The Chinese name of looper is loop, loop, there is an infinite loop in the looper.loop() method, so the main thread will not end running.

#####The relationship between handler and Looper, Message, MessageQueue, thread######

(The picture comes from the Internet)

Two pictures taken from the internet to explain their relationship. After the handler is created, it will have a reference to the messageQueue, the handler sends the message data to the messageQueue, the messageQueue will arrange it to be at the end, the looper has the address of the messageQueue, and keeps pulling out messages from the messageQueue. When the message contains the handler's address, the handler is found, and the looper dispatches the message to the handler for processing. The handler calls its own handleMessage() method to run the user-prepared code to get the data and process it.

Third, several common methods of handler.

  • sendMessage(Message message) , send a Message object to MessageQueue, and execute it immediately after the main thread gets the Message;
  • sendEmptyMessage(int what) , send a Message object containing only what to MessageQueue, and execute it immediately after the main thread gets the Message;
  • sendEmptyMessageDelayed(int what, long delayMillis) , send a Message object containing only what to MessageQueue, and execute it on the main thread after delaying delayMillis milliseconds;
  • sendEmptyMessageAtTime(int what, long uptimeMillis) , send a Message object containing only what to MessageQueue, and execute it on the main thread at a specific time (uptimeMillis);
  • sendMessageDelayed(Message message, long delayMillis) , send a Message object to MessageQueue, and execute it on the main thread after delaying delayMillis milliseconds;
  • sendMessageAtTime(Message message, long uptimeMillis) , send a Message object to MessageQueue, and execute it on the main thread at a specific time (uptimeMillis);
  • sendMessageAtFrontOfQueue(Message message) , send a Message object to the MessageQueue, and place the Message object at the front of the MessageQueue.
  •  

Among them, the acquisition of the Message object can be obtained through the Handler.obtainMessage() method, or through the Message.obtain() method. In fact, the two methods are equivalent.

The following code demonstrates the specific process of sending a Message from the child thread to the main thread by using sendMessageDelayed(Message message, long delayMillis):

Instantiate a Handler object in the main thread and override the handleMessage() method to process the received Message:

Handler handler = new Handler() {
    @Override
    public void handleMessage(Message msg) {
        super.handleMessage(msg);
        Log.d("handleMessage", "收到的 Message 对象 :" + msg.what);
    }
};

Use the handler in the child thread to send a Message:

Message message = Message.obtain();
message.what = 100;
handler.sendMessageDelayed(message, 1000);

In Handler, in addition to using sendMessage() to realize the purpose of data interaction between the main thread and the sub-thread, you can also directly use post() to execute a Runnable object directly in the main thread. Handler has the following API to implement this function:

  • post(Runnable runnable) , send a Runnable object to MessageQueue, and execute it immediately after the main thread gets the Message;
  • postAtTime(Runnable runnable, long uptimeMillis) , send a Runnable object to MessageQueue and execute it on the main thread at a specific time (uptimeMillis);
  • postAtTime(Runnable runnable, Object token, long uptimeMillis) , send a Runnable object and Object object to MessageQueue, and execute it on the main thread at a specific time (uptimeMillis);
  • postDelayed(Runnable runnable, long delayMillis) , send a Runnable object to MessageQueue and execute it on the main thread after delaying delayMillis milliseconds;
  • postAtFrontOfQueue(Runnable runnable) , send a Runnable object to the MessageQueue, and place the Message object at the front of the MessageQueue.                                                         

Fourth, a small case of using handler to pass messages.

The small example I wrote, with the code attached, runs fine on my own mobile phone.

Example 1: Use handler.post(runnable) to switch the main thread of the child thread.

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" 
    android:gravity="center">
<TextView 
    android:id="@+id/textViewId"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="线程名"
    android:textSize="18sp"
    />
<Button 
    android:id="@+id/buttonId"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="点击"
    />
</LinearLayout>
public class MainActivity extends Activity {
	private TextView tv;
	private Button button;
	private Handler handler = new Handler();
	Runnable runnable;
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		button = (Button) findViewById(R.id.buttonId);
		tv = (TextView) findViewById(R.id.textViewId);
		button.setOnClickListener(new ButtonEx());
	}

	class ButtoneEx implements OnClickListener {
		@Override
		public void onClick(View v) {
			Thread t = new ThreadEx();
			t.start();//此处开始执行子线程的run()方法
		}
	}

	class ThreadEx extends Thread {
		@Override
		public void run() {
			// TODO Auto-generated method stub
			super.run();
			String currentStr = Thread.currentThread().getName();
			System.out.println("子线程名字:" + currentStr);
			runnable = new Runnable() {
				@Override
				public void run() {
					// TODO Auto-generated method stub
					String currentString = Thread.currentThread().getName();
					System.out.println("主线程名字:" + currentString);
					tv.setText(currentString);
				}
			};
			handler.post(runnable);//此处切换回主线程中执行runnaable中的run()方法
		}
	}

}

Note: 1. Although runnable is written in the run() method of the child thread, it is executed in the thread initialized by the handler, where the handler is in the main thread, so it switches back to the main thread after handler.post.

2. The handler.post(runnable) method should be written after the runnable method body. If it is written before, it will not be executed.

Example 2: Use handler.sendMessage(Message message) to switch between the child thread and the main thread and transfer message data.

      

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" 
    android:gravity="center">
<TextView 
    android:id="@+id/textViewId"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="你吃饭了吗?"
    android:textSize="18sp"
    />

<TextView
    android:id="@+id/tv_respon"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:textSize="18sp"
    android:layout_marginTop="10dp"
    android:text="未回应" />

<Button 
    android:id="@+id/buttonId"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_marginTop="10dp"
    android:text="点击让她回应你"
    />
<Button 
    android:id="@+id/buttonId2"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_marginTop="10dp"
    android:text="点击让他回应你"
    />
</LinearLayout>
public class handlerMsg extends Activity {
	private TextView tv_respon;
	private Button button, button2;
	private Handler handler;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		// TODO Auto-generated method stub
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		tv_respon = (TextView) findViewById(R.id.tv_respon);
		button = (Button) findViewById(R.id.buttonId);
		button2 = (Button) findViewById(R.id.buttonId2);
		handler = new Handler() {

			@Override
			public void handleMessage(Message msg) {
				// TODO Auto-generated method stub
				super.handleMessage(msg);
				switch (msg.what) {// what标记每个消息
				case 2:
					Person p1 = (Person) msg.obj;// 取出对象强制转换
					String str = p1.name;
					int i = msg.arg1;
					tv_respon.setText(str + "说:我在" + i + "点就吃过了。");
					break;
				case 3:
					Person p2 = (Person) msg.obj;
					String s = p2.name;
					int clock = msg.arg2;// 取出数据
					tv_respon.setText(s + "回应你:我得等到" + clock + "点才能吃。。");
					break;
				}
			}

		};
		button.setOnClickListener(new OnClickListener() {

			@Override
			public void onClick(View arg0) {
				// TODO Auto-generated method stub
				new Thread() {
					@Override
					public void run() {
						// TODO Auto-generated method stub
						super.run();
						try {
							Thread.sleep(7000);// 模拟取数据操作
							Message msg1 = handler.obtainMessage(2);// 生成新消息
							msg1.arg1 = 11;
							Person p1 = new Person();
							p1.setName("俄罗斯小萝莉");
							p1.setSex("女");
							msg1.obj = p1;
							handler.sendMessage(msg1);// 发送消息切换到handler所在的线程执行handleMessage()
						} catch (InterruptedException e) {
							// TODO Auto-generated catch block
							e.printStackTrace();
						}
					}

				}.start();
			}
		});

		button2.setOnClickListener(new OnClickListener() {

			@Override
			public void onClick(View arg0) {
				// TODO Auto-generated method stub
				new Thread() {
					@Override
					public void run() {
						// TODO Auto-generated method stub
						super.run();
						try {
							Thread.sleep(5000);
						} catch (InterruptedException e) {
							// TODO Auto-generated catch block
							e.printStackTrace();
						}// 模拟取数据操作
						Message msg2 = handler.obtainMessage(3);// 生成新消息,what改变
						msg2.arg2 = 12;
						Person p2 = new Person();
						p2.setName("搬砖工人");
						p2.setSex("男");
						msg2.obj = p2;
						handler.sendMessage(msg2);// 发送消息切换到handler所在的线程执行handleMessage()
					}

				}.start();
			}
		});
	}
}

class Person {
	public String name;

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public String getSex() {
		return sex;
	}

	public void setSex(String sex) {
		this.sex = sex;
	}

	public String sex;

}

Note: handleMessage(Message msg) is overridden when the handler is initialized.

   

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325293909&siteId=291194637