Android Handler, androidhandler
In Android development, we often encounter the following situation: After performing an operation on the UI interface, we need to execute a very time-consuming code, for example, if we click a "Download" button on the interface, we need to execute a network request. This is a time-consuming operation because we do not know when to complete it. To ensure that the UI thread is not affected, we will create a new thread to execute our time-consuming code. When our time-consuming operations are completed, We need to update the UI to inform the user that the operations are completed. Therefore, we may 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 to download files"); // Let the Thread DownloadThread sleep for 5 seconds to simulate the time-consuming process of the file Thread. sleep (1, 5000); System. out. println ("file downloaded"); // update the UI MainActivity after the file is downloaded. this. statusTextView. setText ("file download completed");} catch (InterruptedException e) {e. printStackTrace ();}}}}
The code above demonstrates that clicking the "Download" button will start a new thread to execute the actual download operation. After the execution is complete, the UI is updated. However, when the Code MainActivity. this. statusTextView. setText ("file download completed") is actually run, the following error is reported. The system crashes and exits:
Android. view. ViewRootImpl $ CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
The error indicates that only the original thread that created the View can update the View. This error occurs because the View in Android is NOT thread-safe. When the Android Application Starts, a thread is automatically created, that is, the main thread of the program, the main thread is responsible for displaying the UI and dispatching and processing the UI event messages. Therefore, the main thread is also called the UI thread, and statusTextView is created in the UI thread, when we update the statusTextView created in the UI thread in the DownloadThread thread, the above error will naturally be reported. Android UI controls are non-thread-safe. In fact, many platform UI controls are non-thread-safe, such as C. the UI controls in the. Net Framework are also non-thread-safe. Therefore, not only does the Android platform have the problem of updating the UI controls created in the UI thread from a new thread. Different platforms provide different solutions to implement cross-thread and new UI controls. Android introduces the Handler mechanism to solve this problem.
So what is Handler? Handler is a mechanism introduced in Android that allows developers to participate in message loops in processing threads. Each Hanlder is associated with a thread, and each thread maintains a Message Queue MessageQueue. In this way, Handler is actually associated with a message queue. You can use Handler to send the Message and Runnable objects to the MessageQueue (Message Queue) of the Thread associated with the Handler. Then, the Message Queue continuously generates a Message and processes it, after processing, extract the next Message and continue the processing. When a Handler is created, the Handler is bound to the thread for creating the Hanlder. From this moment on, the Hanlder can send the Message and Runnable object to the corresponding Message queue of the Handler. When a Message is retrieved from MessageQueue, the Handler will process it.
Handler can be used for communication among multiple threads. Updating the UI control in the UI thread in another thread is only a typical case in Handler usage. In addition, Handler can do many other things. Each Handler is bound to a thread. Suppose there are two threads ThreadA and ThreadB, and HandlerA is bound to ThreadA. For some reason, when the code in ThreadB is executed somewhere, we need ThreadA to execute some code. Now we can use Handler. We can add some information to HandlerA in ThreadB to inform ThreadA of some processing. As we can see, Handler is the spokesperson of Thread and a bridge between multiple threads. With Handler, we can control another Thread in one Thread to do something.
Handler provides two methods to solve the problem we encountered at the beginning of this article (updating the UI control in the main thread in a new thread), one is through the post method, one is to call the sendMessage method.
A. Use 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 the main thread private Handler uiHandler = new Handler () is automatically bound; @ 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 to download files"); // Let the Thread DownloadThread sleep for 5 seconds to simulate the time-consuming process of the file Thread. sleep (1, 5000); System. out. println ("file download completed"); // After the file is downloaded, update UI Runnable runnable = new Runnable () {@ Override public void run () {System. out. println ("Runnable thread id" + Thread. currentThread (). getId (); MainActivity. this. statusTextView. setText ("file downloaded") ;}}; uiHandler. post (runnable);} catch (InterruptedException e) {e. printStackTrace ();}}}}
We created a Handler member variable uiHandler in the Activity. Handler has a special feature. When executing new Handler (), Handler binds the thread for executing the current Code by default, uiHandler is instantiated in the main thread, so uiHandler is automatically bound to the main thread, that is, the UI thread. After executing the time-consuming code in DownloadThread, we pass a Runnable object to Handler through the post method. Handler will let the main thread execute the code in Runnable when appropriate, in this way, Runnable runs in the main thread and correctly updates the UI in the main thread. The output result is as follows:
The output results show that the thread ID of the Code in Runnable is different from the thread ID of DownloadThread, and is the same as the thread ID of the main thread. Therefore, Handler is executed. after the post (Runnable) Code, the thread that runs the Runnable code is consistent with the thread bound to the Handler, And the thread that executes the Handler. the thread (DownloadThread) of the post (Runnable) code is irrelevant.
B. Use 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 it is automatically bound to the main thread private Handler uiHandler = new Handler () {@ Override public void handleMessage (Message 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 completed"); 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 to download files"); // Let the Thread DownloadThread sleep for 5 seconds to simulate the time-consuming process of the file Thread. sleep (1, 5000); System. out. println ("file download completed"); // After the file is downloaded, the UI Message msg = new Message () is updated; // although the Message constructor is public, we can also use the following two methods to obtain Message/msg = Message through the cyclic object. obtain (uiHandler); // msg = uiHandler. obtainMessage (); // what is a custom Message identifier, so that different messages can be identified based on what in the handleMessage method of Handler, so that we can make different processing operations msg. what = 1; // we can pass in simple data msg to Message through arg1 and arg2. arg1 = 123; msg. arg2 = 321; // We can also pass any data to the Message by assigning the Object type to obj. // msg. obj = null; // You can also use the setData and getData methods to write and read Bundle-type data to and from the Message. // msg. setData (null); // Bundle data = msg. getData (); // send the Message to the corresponding Handler uiHandler. sendMessage (msg);} catch (InterruptedException e) {e. printStackTrace ();}}}}
To communicate with Handler through Message, follow these steps:
1. Override the handleMessage method of Handler and perform different processing operations based on the value of the Message
2. Create a Message object
Although the Message constructor is public, we can also use Message. obtain () or Handler. obtainMessage () to obtain a Message object (Handler. the obtainMessage () actually calls the Message. obtain ()).
3. Set the value of Message
Message. what is our custom Message identifier, so that we can identify different messages in the handleMessage method of Handler, so that we can make different processing operations.
4. Set the data carried by the Message. Simple data can be assigned values through two int-type field arg1 and arg2, and can be read in handleMessage.
5. If the Message needs to carry complex data, you can set the obj field of the Message. obj is of the Object type and can assign any type of data. Alternatively, you can call the setData method of the Message to assign values to the Bundle type data, and you can use the getData method to obtain the Bundle data.
6. We pass the Message to Handler through the Handler. sendMessage (Message) method so that it can be processed in handleMessage.
It should be noted that, if you do not need to judge the Message Type in handleMessage, you do not need to set the value of Message; and it is not necessary to make the Message carry data, data must be carried only when necessary. If the Message must carry data, use arg1, arg2, or both as much as possible. If arg1 and arg2 can be used, do not use obj, because arg1 and arg2 are more efficient.
The program running result is as follows:
From the above we can see that the thread executing handleMessage is the same as the thread creating Handler. In this example, the thread is the main thread. The thread that executes handleMessage has no relationship with the thread that executes uiHandler. sendMessage (msg.
This article describes how to use Handler in Android. Later, I will write an article to describe the internal implementation principle of Handler, including logoff and MessageQueue.
Copyright Disclaimer: This article is an original article by the blogger and cannot be reproduced without the permission of the blogger.