In Android development, we are often confronted with a situation in which a time consuming code is executed after an operation in the UI interface, such as when we click on a "Download" button on the interface, so we need to execute the network request, which is a time-consuming operation because we do not know when to complete. To ensure that the UI thread is not affected, we create a new thread to execute our time-consuming code. When our time consuming operation is complete, we need to update the UI interface to tell the user that the operation is complete. So we might write the following code:
Package Ispring.com.testhandler;
Import android.app.Activity;
Import Android.os.Bundle;
Import Android.view.View;
Import Android.widget.Button;
Import Android.widget.TextView;
public class Mainactivity extends activity implements Button.onclicklistener {private TextView statustextview = null;
@Override protected void OnCreate (Bundle savedinstancestate) {super.oncreate (savedinstancestate);
Setcontentview (R.layout.activity_main);
Statustextview = (TextView) Findviewbyid (R.id.statustextview);
Button Btndownload = (button) Findviewbyid (r.id.btndownload);
Btndownload.setonclicklistener (this);
@Override public void OnClick (View v) {downloadthread downloadthread = new Downloadthread ();
Downloadthread.start ();
Class Downloadthread extends thread{@Override public void Run () {try{System.out.println ("Start downloading Files");
This lets the thread downloadthread hibernate for 5 seconds, simulating the time-consuming process of the file Thread.Sleep (5000);
System.out.println ("File download Complete"); Update UI MAINACTIVITY.THIS.S after file download completesTatustextview.settext ("File download Complete");
}catch (interruptedexception e) {e.printstacktrace ();
}
}
}
}
The code above shows that clicking the download button launches a new thread to perform the actual download operation, and then updates the UI interface after the execution completes. However, when actually running to code MainActivity.this.statusTextView.setText ("File Download Complete"), the error is as follows, system crashes exit:
android.view.viewrootimpl$ Calledfromwrongthreadexception:only the original thread that created a view hierarchy can touch it views.
The error means that only the original thread that created the view can update the view. The reason for this error is that the view in Android is not thread safe, and when the Android application starts, it automatically creates a thread, the main thread of the program, the main thread responsible for the display of the UI, the distribution of the UI event messages, and so on, so the main thread is also called the UI thread The Statustextview is created in the UI thread and will naturally report errors when we update the Statustextview created in the UI thread in the Downloadthread thread. The Android UI controls are not thread-safe, but many of the platform's UI controls are not thread-safe, such as the UI controls in the. Net framework of C # are also not thread-safe. So not only is there a problem with the Android platform to update UI controls created in the UI thread from a new thread. Different platforms offer different solutions to implement cross threading and new UI controls, and Android introduces the handler mechanism to address this problem.
So what exactly is handler? Handler is a mechanism introduced in Android that allows developers to participate in the processing of message loops in threads. Each hanlder has a thread associated with it, and a Message Queuing MessageQueue is maintained inside each thread, so handler actually has a message queue associated with it. The message and Runnable objects can be sent to the MessageQueue (Message queue) of the thread to which the handler is associated by handler, which is then kept in a loop and is processed, After processing, take out the next message, continue processing, cycle. When a handler is created, the handler binds the thread that is currently creating the Hanlder. From this point on, the Hanlder can send messages and runnable objects to the handler message queue, which will be processed by handler when it is taken out of the MessageQueue.
Handler can be used to communicate between multithreading, and to update UI controls in the UI thread in another thread is just a typical case of handler use, handler can do many other things. Each handler is bound to a thread, assuming there are two threads Threada and threadb, and Handlera bound Threada, and for some reason we need to get threadb to execute some code for certain reasons, in the case of code in Threada. At this point we can use handler, and we can add some information to the Handlera in the threadb to tell Threada that some processing should be done. From this we can see that handler is the voice of thread, is a bridge between multithreading communication, through handler, we can control another thread in one thread to do something.
Handler provides two ways to solve the problem we encountered at the outset of this article (updating the UI controls in the main thread in a new threads), either through the post method or by invoking the SendMessage method.
A. Using the Post method, the code is as follows:
Package Ispring.com.testhandler;
Import android.app.Activity;
Import Android.os.Bundle;
Import Android.os.Handler;
Import Android.view.View;
Import Android.widget.Button;
Import Android.widget.TextView;
public class Mainactivity extends activity implements Button.onclicklistener {private TextView statustextview = null;
Uihandler is created in the main thread, so automatically binds the main thread private Handler Uihandler = new Handler ();
@Override protected void OnCreate (Bundle savedinstancestate) {super.oncreate (savedinstancestate);
Setcontentview (R.layout.activity_main);
Statustextview = (TextView) Findviewbyid (R.id.statustextview);
Button Btndownload = (button) Findviewbyid (r.id.btndownload);
Btndownload.setonclicklistener (this);
System.out.println ("Main thread id" + thread.currentthread (). GetId ());
@Override public void OnClick (View v) {downloadthread downloadthread = new Downloadthread ();
Downloadthread.start (); Class Downloadthread extends thread{@Override public void Run () {try{SYSTEM.OUT.PRINTLN ("Downloadthread id" + thread.currentthread (). GetId ());
System.out.println ("Start downloading Files");
This lets the thread downloadthread hibernate for 5 seconds, simulating the time-consuming process of the file Thread.Sleep (5000);
System.out.println ("File download Complete"); Update UI Runnable Runnable = new Runnable () {@Override public void run () {System.out.println ("R") after file download completes
Unnable thread id "+ thread.currentthread (). GetId ());
MainActivity.this.statusTextView.setText ("File download Complete");
}
};
Uihandler.post (runnable);
}catch (interruptedexception e) {e.printstacktrace ();
}
}
}
}
We created a Handler member variable in the activity Uihandler,handler that, when the new Handler () is executed, Handler binds the thread that the current code executes by default. We instantiate the Uihandler in the main thread, so Uihandler automatically binds the main thread, the UI thread. When we execute the time-consuming code in the Downloadthread, we pass a Runnable object to the handler via the Post method, and handler will let the main thread execute the code in the runnable at the right time, This runnable is executed in the main thread, which correctly updates the UI in the main thread. The following are the output results:
As you can see from the output, the code in Runnable executes a different thread ID than the Downloadthread thread ID, which is the same as the thread IDs of the main thread, so we see that after executing the handler.post (Runnable) code, The thread that runs the Runnable code is consistent with the thread that handler is bound to, regardless of the thread (Downloadthread) that executes the code for Handler.post (Runnable).
B. Using the SendMessage method, the code is as follows:
Package Ispring.com.testhandler;
Import android.app.Activity;
Import Android.os.Bundle;
Import Android.os.Handler;
Import Android.os.Message;
Import Android.view.View;
Import Android.widget.Button;
Import Android.widget.TextView;
public class Mainactivity extends activity implements Button.onclicklistener {private TextView statustextview = null; Uihandler is created in the main thread, so automatically binds the main thread private Handler Uihandler = new Handler () {@Override public void Handlemessage msg) {switch (msg.what) {case 1:system.out.println ("Handlemessage thread id" + thread.currentthread (). getId
());
System.out.println ("MSG.ARG1:" + msg.arg1);
System.out.println ("MSG.ARG2:" + msg.arg2);
MainActivity.this.statusTextView.setText ("File download Complete");
Break
}
}
};
@Override protected void OnCreate (Bundle savedinstancestate) {super.oncreate (savedinstancestate);
Setcontentview (R.layout.activity_main);
Statustextview = (TextView) Findviewbyid (R.id.statustextview); Button Btndownload = (Button) Findviewbyid (r.id.btndownload);
Btndownload.setonclicklistener (this);
System.out.println ("Main thread id" + thread.currentthread (). GetId ());
@Override public void OnClick (View v) {downloadthread downloadthread = new Downloadthread ();
Downloadthread.start (); Class Downloadthread extends thread{@Override public void Run () {try{System.out.println ("Downloadthread
ID "+ thread.currentthread (). GetId ());
System.out.println ("Start downloading Files");
This lets the thread downloadthread hibernate for 5 seconds, simulating the time-consuming process of the file Thread.Sleep (5000);
System.out.println ("File download Complete");
Update UI Message msg = new Message () after file download is complete;
Although the constructor of the message is public, we can get the message//msg = Message.obtain (Uihandler) through the Loop object in the following two ways;
msg = Uihandler.obtainmessage ();
What is our custom identifier for a message so that we can identify//out different message according to what in the handler Handlemessage method so that we can make different processing msg.what = 1;
We can pass arg1 and arg2 to the message into a simple data MSG.ARG1 = 123;
MSG.ARG2 = 321; We can also assign a value to obj OThe Bject type passes the message into any data//msg.obj = null;
We can also write and read the bundle type of data//msg.setdata (NULL) to the message through the SetData method and the GetData method;
Bundle data = Msg.getdata ();
Send the message to the corresponding handler uihandler.sendmessage (msg);
}catch (interruptedexception e) {e.printstacktrace ();
}
}
}
}
The steps for communicating with handler through the message are:
- 1. Rewrite the handler Handlemessage method and perform different processing according to the what value of the message
- 2. Create a Message object
- Although the message's constructor is public, we can also get a Message object (Handler.obtainmessage () by Message.obtain () or handler.obtainmessage (). Inside actually called the Message.obtain ()).
- 3. Set the what value of the message
- Message.what is our custom identifier for a message so that different message can be identified according to the what in the handler Handlemessage method so that we can make different processing operations.
- 4. Set the data carried by the message, the simple data can be assigned by two field arg1 and arg2 of type int, and can be read in Handlemessage.
- 5. If the message needs to carry complex data, you can set the obj field for the message, obj is type object and can be given any type of data. Alternatively, a bundle type of data can be assigned by invoking the SetData method of the message, and the bundle data can be obtained by the GetData method.
- 6. We use the Handler.sendmessage (message) method to pass the message into the handler to process it in handlemessage.
It is important to note that if you do not need to determine the message type in the Handlemessage, you do not have to set the what value of the message, and it is not necessary to have the message carry data, only when it is needed, to carry data If you do need to have the message carry data, try to use arg1 or arg2 or both, and don't use obj if you can solve it with arg1 and arg2, because it's more efficient with arg1 and arg2.
The results of the program's operation are as follows:
As we can see from the above, the thread that executes the handlemessage is the same thread as the thread that created the handler, in this case, the main thread. The thread executing the handlemessage is not related to the thread that executes Uihandler.sendmessage (msg).
This article is mainly on Android handler role in how to use a preliminary introduction, if you want to understand the internal implementation of handler principle, you can see the next post "deep source analysis of Android Handler,message, Messagequeue,looper ".