Handler mechanism learning Summary
I. Functions of handler
First explain why handler is needed. The main thread needs to execute a time-consuming operation and update the UI accordingly based on the operation results. However, Android reports an ANR error when the time-consuming operation cannot be completed within five seconds. The cause of this ANR error is that the main thread does only one thing in five seconds (and it may not be completed ), then we cannot respond to other user input events. The solution is to let the sub-thread do this time-consuming task and then return the result to the main thread, the main thread updates the corresponding UI Based on the returned results (Note: The subthread does not interact with the customer, so it does not directly process user input events, so it doesn't matter if you do one thing for more than five seconds ).
Here our problem is transformed into a thread communication problem in Android: the main thread needs to send a message to tell the sub-thread what to do. After the sub-thread completes, it needs to feedback the work result to the main thread, the main thread needs to handle the problem based on the feedback from the subthread. All these work is done by handler, which is one of the reasons for Handler's existence.
2. handler process
How can we use handler to complete inter-thread communication? You can create an instance, send event messages, accept event processing results, and update the UI.
1. We should create a handler instance in the main thread.
Handler handler = new handler (); handler = new handler (loop); // loop is a loop object
Handler also has two constructor methods, which are not listed here. It should be pointed out that the two methods listed here have essential differences, and we will discuss the differences later.
2. We encapsulate what we want to do into a message object and send this message object through the handler method.
Methods for sending messages in Handler
post(Runnable) postAtTime(Runnable,long) postDelayed(Runnable long) sendEmptyMessage(int) sendMessage(Message) sendMessageAtTime(Message,long) sendMessageDelayed(Message,long)
3. Public voidhandlemessage (Message MSG)
Here we can accept the results returned after the sub-thread completes the event. We can use the override method to update the UI. Of course, this method is not required if it is not required, it depends on the specific needs of your application.
Here we can see that handler is very simple to use. Next we will look at an example: Our UI has two buttons, one for downloading, which will be very time-consuming, we use the thread sleep method to simulate this time-consuming process. We make it sleep for 6 seconds, and the other button is very simple, just display information through toast.
The layout file is very simple. The Code is as follows:
Public class handler01activityextends activity {private int Index = 0; @ override public void oncreate (bundlesavedinstancestate) {super. oncreate (savedinstancestate); setcontentview (R. layout. main); // two buttons, dnloadbt and caculatebt button dnloadbt = (button) findviewbyid (R. id. download); button caculatebt = (button) findviewbyid (R. id. caculate); // bind an event to a button: the event is sent to the runnable run method in the following content. Andler, which is sent out using the POST method. Dnloadbt. setonclicklistener (newview. onclicklistener () {public void onclick (view v) {handler. post (runnable); // Step 2: send the message through handler, and the message carries the time-consuming event to be processed}); // bind the event to this button: use toast to display a sentence caculatebt. setonclicklistener (newview. onclicklistener () {public void onclick (view v) {// todoauto-generated method stub toast. maketext (handler01activity. this, "OK" + (index ++), toast. length_short ). show () ;}}) ;}// Step 1: Create a handler instance in the main thread. Note the constructor called here !! Handler handler = new handler (); // The time-consuming event to be processed is described here: thread. sleep (6000), and then use toast to display a sentence runnable = new runnable () {public void run () {// todo auto-generatedmethod stub try {thread. sleep (6000);} catch (interruptedexceptione) {// todoauto-generated Catch Block System. out. println (e );}}};}
This example is a simple analysis.
If you perform the following operations: First press dnloadbt, then immediately press caculatebt, that is, run the command to execute the time-consuming operation, and then immediately execute the operation that is not time-consuming. We know that if handler is not used, this kind of operation will certainly report ANR error, because the application needs 6 s to execute the first operation, and the corresponding second operation can only be 6 s later, the user's second operation cannot get a response within 5s, although the second operation is very simple.
Now we use the handler mechanism, can we avoid ANR errors? Neither can the results, which makes me very confused, because it does not play its due role. To solve this problem, we also need to have a deeper understanding of the handler mechanism.
Iii. Handler Message Management Mechanism
Here, I would like to raise a few questions: Where can the main thread send messages through the hanler message distribution method? How do subthreads obtain these messages? What can I do after obtaining these messages to complete the tasks explained by the main thread? After the task is completed, the subthread needs to feedback the result to the main thread, and the main thread will update the UI using the handlemessage method. But who will do this? How can I know that the subthread has completed the task ......
Fig.1 simple Handler
Figure 1 describes the following information: After a handler instance is created in the main thread, Handler obtains a logoff object, which also has a message queue. The handler finally sends messages to the message queue in the logoff State for all the message sending methods called by the main thread. Logoff's loop method extracts messages from the beginning of the message queue, classifies the messages, and submits them to the corresponding method for processing (the detailed process is omitted ). Here there is a question that needs to be carefully considered, that is, how the logoff in Handler comes from. Compare the two handler creation methods.
1. Handler () constructor
Publichandler () {// obtain the logoff object of the current thread. By default, The logoff object of the current thread will be associated. // If the logoff object is not set in this thread, an exception will be thrown. myloue (); // important. MQ associated with logoff is directly used as its own MQ. Therefore, its message will be sent to the MQ associated with logoff, and mqueue = mloue. mqueue; mcallback = NULL ;}
As you can see, when handler is created by this method, the logoff object is obtained through the logoff. mylogoff () method. The annotation of this method is as follows: Return thelow.object associated with the current thread. returns NULL if the calling thread is notassociated with a logoff. that is, it will return the logoff associated with the current thread. If the current thread is not associated with any logoff, it will return null. One thing to note: the main thread must be associated with a logoff (the association process is completed by the system. For details, see activitythread. java), and the Sub-thread is generally not associated with logoff, so we can call the handler constructor in the main thread, but calling this method in a sub-thread may throw an exception.
Now I can explain the questions in the previous instance program: because the program creates a handler instance through this constructor, The logoff obtained by handler is actually the main thread, the message queue is also the main thread, so handler only delivers the message to the Message Queue of the main thread. The main thread is actually processing the time-consuming message, so an ANR error is reported.
2. Handler (low.lofter) constructor
public Handler(Looper looper) { mLooper = looper; mQueue = looper.mQueue; mCallback = null;}
This construction method is very simple. If a logoff object is passed externally, the handler message will naturally be sent to the Message Queue of The logoff object, messages are also processed by the process of the logoff object. Therefore, this is the asynchronous processing method we really want. Only in this way can we avoid the ANR error encountered by our previous instance. Next we will analyze how to use this method to solve our problem.
4. asynchronous Processing Using Handler
Since it is clear that we should call the handler (looperlooper) constructor to create a handler instance, and The logoff object should be non-main thread. So our idea should be: first create a sub-thread, then associate the sub-thread with a logoff object, and then hand over the logoff object to handler, in this way, the messages sent by handler can be handed over to the subthread for processing.
Creating a sub-thread is simple. The key is to create a logoff object.
private Looper() { mQueue = new MessageQueue(); mRun = true; mThread = Thread.currentThread(); }
The logoff class only has this constructor, but it is private. Therefore, you cannot use this method to create a logoff object. This class provides another method to help us. Its method body calls this unique constructor.
Public static void prepare () {If (sthreadlocal. Get ()! = NULL) {// an attempt to create a logoff in a logoff thread will throw an exception throw new runtimeexception ("only one logoff may be created perthread");} sthreadlocal. set (New logoff (); // core statement}
In this way, only Looper is set for this thread, and the looper object of this subthread can be truly obtained by calling Looper. mylooper () method. At this point, it is not over yet. We still need to call the logoff loop method so that this logoff can be used to continuously extract and execute messages from the message queue.
As a matter of fact, we don't need to make it so complicated, because Android already provides a class in which all the complicated work has been completed and we only need to use it. This class is handlerthread, which inherits from thread.
HandlerThread handlerThread = newHandlerThread("yutao");handlerThread.start();Handler UIhandler = new Handler(handlerThread.getLooper());
With the three lines of code, we create a sub-thread, and have the sub-thread with its own logoff object, and pass the object to the handler. When the handler sends a message again, it will be handed over to the subthread for asynchronous processing.
V. Summary
The following briefly summarizes the associations among thread, handler, logoff, and messagequeue.
First, a thread can have only one Looper or no looper; the main thread generally has Looper, which is created by the system and can be directly used; the threads we create do not have logoff. We need to create them ourselves and call the loop method to start them. Android provides the handlerthread class to facilitate handler asynchronous processing, it is a thread with logoff.
Secondly, one thread can create one or more handler. Each handler must have one logoff, and a logoff is bound to the handler during handler creation; there are two ways to create handler in the thread (in fact there are four constructor methods. Here we only care about the issues we are discussing, so we do not consider the other two): new handler () and new handler (loop logoff); the first figure below shows that the Logoff of the thread for creating the handler is selected by default because the logoff is not specified in the first method, however, if this thread is not associated with logoff, an exception will be thrown, and the handler in this way cannot implement asynchronous processing, so what is the purpose of this method, I do not understand for the time being, message Or runnable can be arranged online
It is executed somewhere in a main thread. When it is created in the second method, it can be bound to the Logoff of another thread, so that asynchronous processing can be implemented. This method is more common for applications, of course, the premise is that the thread has been associated with the logoff, otherwise it will throw an exception. More commonly, We will write a subthread to inherit the handlerthread.
Third, a logoff corresponds to a messagequeue one by one, which is a one-to-one relationship. All messages of the handler bound to the logoff will be sent to this message queue, all messages that handler can process through the handlemessage method are obtained from this message queue.
Fourth, as mentioned above, a handler must be bound to a loose and bound when a handler is created. But a loose can be shared by multiple handler. This problem may exist, that is, if multiple handler sends messages to the same loose, that is, to the same message queue, will the messages obtained by handler's handlemessage method be obtained by themselves? This problem is solved in this way: each message has a member variable handler target. Therefore, when a handler sends a message, it indicates that the target object of the message is the handler, in this way, we can determine the handler of a message.
Fig.2 use the newhandler () method to create fig.3 use the newhandler (lofter) method to create
Finally, we will discuss the messages in handler. Messages in handler can be divided into two types: runnable and runnable. When handler posts a runnable object, it is encapsulated into a message. The encapsulation process is simple: create a message with an object and assign a value to its member variable runnablecallback. The fundamental difference between the two types of messages is that when queuing in the message queue is its turn to itself, the message with runnable will then execute its run method, and then it will end, messages without runnable will be directly sent to the handlemessage method of handler for processing. In my understanding, when the main thread is time-consuming and needs to be processed, put it in runnable and put it in the message queue for the subthread for processing. After the subthread finishes processing, A message without runnable can be sent to the queue. The main thread can obtain the sub-thread's work results in the handlemessage and update the UI accordingly (this statement is not verified and is personally understood ).