In Android asynchronous processing 1: Using Thread + Handler to update the UI of a non-UI Thread.
Overview: Android uses the message mechanism to implement inter-thread communication. A thread establishes its own message loop through logoff. MessageQueue is a FIFO message queue, and logoff is responsible for extracting messages from MessageQueue, and distribute it to the specified Handler object of the message. The Handler object is bound to the logoff local variable of the thread, which encapsulates the interfaces for sending messages and processing messages.
Example: Before introducing the principle, we will first introduce an example of Android thread communication, in this example, the "hello" message is sent from the main thread to another thread named "CustomThread" after the button is clicked.
Code download
LooperThreadActivity. java
[Java]
1. package com. zhuozhuo;
2.
3. import android. app. Activity;
4. import android. OS. Bundle;
5. import android. OS. Handler;
6. import android. OS. logoff;
7. import android. OS. Message;
8. import android. util. Log;
9. import android. view. View;
10. import android. view. View. OnClickListener;
11.
12. public class LooperThreadActivity extends Activity {
13./** Called when the activity is first created .*/
14.
15. private final int MSG_HELLO = 0;
16. private Handler mHandler;
17.
18. @ Override
19. public void onCreate (Bundle savedInstanceState ){
20. super. onCreate (savedInstanceState );
21. setContentView (R. layout. main );
22. new CustomThread (). start (); // create and start the CustomThread instance
23.
24. findViewById (R. id. send_btn). setOnClickListener (new OnClickListener (){
25.
26. @ Override
27. public void onClick (View v) {// send a message when you click the interface
28. String str = "hello ";
29. Log. d ("Test", "MainThread is ready to send msg:" + str );
30. mHandler. obtainMessage (MSG_HELLO, str). sendToTarget (); // send a message to the CustomThread instance
31.
32 .}
33 .});
34.
35 .}
36.
37.
38.
39.
40.
41. class CustomThread extends Thread {
42. @ Override
43. public void run (){
44. // steps for establishing a message loop
45. logoff. prepare (); // 1. initialize Logoff
46. mHandler = new Handler () {// 2. Bind the handler to The logoff object of the CustomThread instance
47. public void handleMessage (Message msg) {// 3. Define the Message processing method.
48. switch (msg. what ){
49. case MSG_HELLO:
50. Log. d ("Test", "CustomThread receive msg:" + (String) msg. obj );
51 .}
52 .}
53 .};
54. logoff. loop (); // 4. Start the message loop.
55 .}
56 .}
57 .}
Main. xml
[Html]
1. <? Xml version = "1.0" encoding = "UTF-8"?>
2. <LinearLayout xmlns: android = "http://schemas.android.com/apk/res/android"
3. android: orientation = "vertical"
4. android: layout_width = "fill_parent"
5. android: layout_height = "fill_parent"
6.>
7. <TextView
8. android: layout_width = "fill_parent"
9. android: layout_height = "wrap_content"
10. android: text = "@ string/hello"
11./>
12. <Button android: text = "Send message" android: id = "@ + id/send_btn" android: layout_width = "wrap_content" android: layout_height = "wrap_content"> </Button>
13. </LinearLayout>
Log print result:
Principle:
We can see that there are four steps to establish a message loop for a thread:
1. initialize Logoff
2. Bind handler to The logoff object of the CustomThread instance
3. Define the Message Processing Method
4. Start the message loop
Next we will take this example as a clue to go deep into the Android source code to explain how the Android Framework establishes a message loop and distributes messages.
1. initialize lorule: lorule. prepare ()
Logoff. java
[Java]
1. private static final ThreadLocal sThreadLocal = new ThreadLocal ();
2. public static final void prepare (){
3. if (sThreadLocal. get ()! = Null ){
4. throw new RuntimeException ("Only one logoff may be created per thread ");
5 .}
6. sThreadLocal. set (new logoff ());
7 .}
When a thread calls the logoff Static Method prepare (), the thread creates a logoff object and puts it into the local variable of the thread, this variable is not shared with other threads (about ThreadLocal ). Let's take a look at the logoff () constructor:
Logoff. java
[Java]
1. final MessageQueue mQueue;
2. private logoff (){
3. mQueue = new MessageQueue ();
4. mRun = true;
5. mThread = Thread. currentThread ();
6 .}
You can see that a message queue object (mQueue) is created in the logoff constructor. the prepare () thread creates a message loop object (message loop is not started yet ).
2. Bind handler to The logoff object of the CustomThread instance: mHandler = new Handler ()
Handler. java
[Java]
1. final MessageQueue mQueue;
2. final logoff mlogoff;
3. public Handler (){
4. if (FIND_POTENTIAL_LEAKS ){
5. final Class <? Extends Handler> klass = getClass ();
6. if (klass. isAnonymousClass () | klass. isMemberClass () | klass. isLocalClass ())&&
7. (klass. getModifiers () & Modifier. STATIC) = 0 ){
8. Log. w (TAG, "The following Handler class shocould be static or leaks might occur:" +
9. klass. getCanonicalName ());
10 .}
11 .}
12.
13. mlogoff = logoff. mylogoff ();
14. if (mloiter = null ){
15. throw new RuntimeException (
16. "Can't create handler inside thread that has not called logoff. prepare ()");
17 .}
18. mQueue = mloue. mQueue;
19. mCallback = null;
20 .}
Handler gets the thread Message Queue through mLooper = Looper. myLooper (); binds it to the local variable Looper of the thread, and Handler obtains the Message Queue through mQueue = mloue. mQueue. In this case, Handler is bound to the Message Queue of the thread that created the Handler object.
3. Define the Message Processing Method: Override public void handleMessage (Message msg ){}
Subclass needs to override this method to implement the processing method after receiving the message.
4. Start the message loop: logoff. loop ()
All preparations are ready. It is time to start the message loop! Logoff's static method loop () implements a message loop.
Logoff. java
[Java]
1. public static final void loop (){
2. lodomainme = mylodomain ();
3. MessageQueue queue = me. mQueue;
4.
5. // Make sure the identity of this thread is that of the local process,
6. // and keep track of what that identity token actually is.
7. Binder. clearCallingIdentity ();
8. final long ident = Binder. clearCallingIdentity ();
9.
10. while (true ){
11. Message msg = queue. next (); // might block
12. // if (! Me. mRun ){
13. // break;
14 .//}
15. if (msg! = Null ){
16. if (msg.tar get = null ){
17. // No target is a magic identifier for the quit message.
18. return;
19 .}
20. if (me. mLogging! = Null) me. mLogging. println (
21. ">>>>> Dispatching to" + msg.tar get + ""
22. + msg. callback + ":" + msg. what
23 .);
24. msg.tar get. dispatchMessage (msg );
25. if (me. mLogging! = Null) me. mLogging. println (
26. "<Finished to" + msg.tar get + ""
27. + msg. callback );
28.
29. // Make sure that during the course of dispatching
30. // identity of the thread wasn' t upted.
31. final long newIdent = Binder. clearCallingIdentity ();
32. if (ident! = NewIdent ){
33. Log. wtf ("logoff", "Thread identity changed from 0x"
34. + Long. toHexString (ident) + "to 0x"
35. + Long. toHexString (newIdent) + "while dispatching"
36. + msg.tar get. getClass (). getName () + ""
37. + msg. callback + "what =" + msg. what );
38 .}
39.
40. msg. recycle ();
41 .}
42 .}
43 .}
While (true) indicates the "loop" in the message loop. logoff calls queue. next () in the loop body to obtain the next message to be processed in the message queue. When msg! = Nulland msg.tar get! = For null, msg.tar get. dispatchMessage (msg) is used. After the message is sent, msg. recycle () is called to recycle the message.
Msg.tar get is a handler object, indicating the handler object to process the message. The void dispatchMessage (Message msg) method of Handler is as follows:
Handler. java
[Java]
1. public void dispatchMessage (Message msg ){
2. if (msg. callback! = Null ){
3. handleCallback (msg );
4.} else {
5. if (mCallback! = Null ){
6. if (mCallback. handleMessage (msg )){
7. return;
8 .}
9 .}
10. handleMessage (msg );
11 .}
12 .}
Visible when msg. when callback = null and mCallback = null, handleMessage (msg) is used to process the message. As mentioned above, the subclass overwrites this method to implement the specific processing process of the message.
Conclusion: from the above analysis, we can see that the core of the message loop is logoff. logoff holds the MessageQueue object of the Message Queue. A thread can set logoff as a local variable of the thread, this is equivalent to creating a message queue for this thread. The role of Handler is to encapsulate the process of sending and processing messages, so that other threads can send messages to the thread that creates Handler only by operating Handler. From this we can know that in the previous article "Android asynchronous processing 1: Using Thread + Handler to implement non-UI Thread UI update interface" Handler (in the public static final void main (String [] args of ActivityThread) so we can send messages to the handler of the UI thread in other threads to update the UI.
From lzc's column