Handler detail series (1) -- Detailed description of Handler asynchronous message mechanism (see figure)
MainActivity is as follows:
Package cc.cn; import android. OS. bundle; import android. OS. handler; import android. OS. logoff; import android. OS. message; import android. util. log; import android. app. activity;/*** Demo Description: * Android asynchronous message mechanism analysis (see figure) ** ===================================================== =============================** introduction of the problem: * When Handler handler = new Handler () is directly called in a child thread, the following error occurs: * Can't create handler inside thread that has not called logoff. prepare (). * Since the error is reported when the Handler constructor is called, let's look at the source code of the constructor. * public Handler () {* if (FIND_POTENTIAL_LEAKS) {* final Class
Klass = getClass (); * if (klass. isAnonymousClass () | klass. isMemberClass () | klass. isLocalClass () * & (klass. getModifiers () & Modifier. STATIC) = 0) {* Log. w (TAG, "The following Handler class shoshould be * static or leaks might occur:" + klass. getCanonicalName (); *} ** mlogoff = logoff. myLooper (); * if (mloexception = null) {* throw new RuntimeException (* "Can't create handler inside thread that Has not called logoff. prepare () "); *} * mQueue = mloue. mQueue; * mCallback = null; *} ** from the above Code mloue = logoff. mylogoff () Can be seen that when mloiter = null, the error * Can't create handler inside thread that has not called logoff will be reported. prepare (). * ** continue reading The logoff. source code of mylogoff. ** Return the loginobject associated with the current thread. ** Returns null if the calling thread is not associated with a logoff. * public stat Ic logoff mylogoff () {* return sThreadLocal. get (); *} * See the two lines in the source code: * return The logoff associated with the current thread. * If the current thread does not have a logoff associated with it, null is returned. ** check whether the preceding error Can't create handler inside thread that has not called loled. prepare (). * We are prompted to call logoff. prepare (). * write this in the Child thread: * logoff. prepare (); * Handler handler = new Handler (); * The error message disappears. ** since logoff is called. prepare () can eliminate this error. * Let's take a look at logoff. prepare () source code. **/** Initialize the curr Ent thread as a loose. ** This gives you a chance to create handlers that then reference ** this logoff, before actually starting the loop. be sure to call ** {@ link # loop ()} after calling this method, and end it by calling ** {@ link # quit ()}. * public static void prepare () {* if (sThreadLocal. get ()! = Null) {* throw new RuntimeException ("Only one Looper may be created per thread"); *} * sThreadLocal. set (new LOE (); *} * 1 from the annotation document, we can see the role of the prepare () method: * use A loe to initialize the current thread. * 2 you must call the prepare () method before calling the loop () method. * 3 Note the exception message of this method: * Only one Looper may be created per thread * Each thread has Only one Looper !!!! ** Code in this method: sThreadLocal. set (new loue (); * Looper constructor: * private Looper () {* mQueue = new MessageQueue (); * mRun = true; * mThread = Thread. currentThread (); *} * you can see that a logoff is saved for sThreadLocal. * therefore, sThreadLocal is called in the above mylogoff () method. get () is no longer empty. * ** at this point, Handler should be used in a child Thread: * class LooperThread extends Thread {* public Handler mHandler; ** public void run () {* loler. prepare (); ** mHand Ler = new Handler () {* public void handleMessage (Message msg) {* // process incoming messages here *}; ** logoff. loop (); *} * This Code is also recommended by Google. * ** logoff is not called when Handler is used in the UI thread of MainActivity. prepare (); * Why? * Because the UI thread is the main thread, the system has automatically called logoff for us. prepare () method. * I will not go into details here. * ** the preceding sections discuss the use of threads and loopers. The following sections describe the message sending and processing processes. * The most common method: * handler. sendMessage (message); * So how does handleMessage (Message msg) obtain the sent message ??? * Handler can send messages in several methods, but several methods except sendMessageAtFrontOfQueue (Message msg) * will eventually call the sendMessageAtTime (Message msg, long uptimeMillis) method: ** public boolean sendMessageAtTime (Message msg, long uptimeMillis) {* boolean sent = false; * MessageQueue queue = mQueue; * if (queue! = Null) {* msg.tar get = this; * sent = queue. enqueueMessage (msg, uptimeMillis); *} * else {* RuntimeException e = new RuntimeException (* this + "sendMessageAtTime () called with no mQueue"); * Log. w ("logoff", e. getMessage (), e); *} * return sent; *} ** there are two important codes in this method: * 1 msg.tar get = this; * set target for msg. * this here is of course the current Handler object! * 2 sent = queue. enqueueMessage (msg, uptimeMillis); * put the message into the message queue. * The queue (mQueue) Here is the mQueue In The logoff constructor. * The enqueueMessage (msg, uptimeMillis) method has a queue. * messages with the shortest trigger time are placed at the top of the queue, and messages with the longest trigger time are placed at the end of the queue. * if you call the sendMessageAtFrontOfQueue () method to send a message, it will call the enqueueMessage (msg, uptimeMillis) * to enable the message to queue only when the delay time is 0, that is, it will be inserted into the queue header. * ** this is the queue operation for messages. How can a message be sent out of the queue? * It depends on the logoff loop () method * public static final void loop () {* lodomainme = myloath(); * MessageQueue queue = me. mQueue; * while (true) {* Message msg = queue. next (); // might block * if (msg! = Null) {* if (msg.tar get = null) {* return; *} * if (me. mLogging! = Null) me. mLogging. println (* ">>>>> Dispatching to" + msg.tar get + "" + msg. callback + ":" + msg. what); * msg.tar get. dispatchMessage (msg); * if (me. mLogging! = Null) me. mLogging. println (* "<Finished to" + msg.tar get + "" + msg. callback); * msg. recycle (); *} * In this method is an endless loop while (true), that is, logoff is always polling the Message Queue (MessageQueue) * There are two important codes in this method: * 1 Message msg = queue. next (); * queue. the output column of the next () message queue. * 2 msg.tar get. dispatchMessage (msg); * call the dispatchMessage () method of the target in msg. * What is target? * See sendMessageAtTime (Message msg, long uptimeMillis). We can see that * target is Handler !!!! The dispatchMessage method of Handler is called back here, so the message is sent to the corresponding Handler. * Next, let's take a look at the Handler's dispatchMessage (Message msg) method: ** public void dispatchMessage (Message msg) {* // 1 message callback * if (msg. callback! = Null) {* handleCallback (msg); *} else {* // 2 handler's callback * if (mCallback! = Null) {* if (mCallback. handleMessage (msg) {* return; *} * // handleMessage () * handleMessage (msg) of 3 Handler; **} ** the CallBack involved is: * public interface Callback {* public boolean handleMessage (Message msg); *} * One of Handler's constructor methods is: * Handler handler = new Handler (callback ); * In dispatchMessage (Message msg), CallBack is involved. * In most cases, the callBack of message and Handler is empty. * therefore, dispatchMessage (Messa Ge msg) method: * this is back to what we are most familiar. * *** ============================================== ===============================** Android asynchronous message mechanism mainly involves: * Thread Handler low.messagequeue * their relationships are as follows: * 1 logoff. prepare (); * 1.1 generates the corresponding logoff for the current thread. * A loose contains three variables: * mQueue ----> MessageQueue * mRun ------> true * mThread ---> the current thread * 1.2 saves the loose to ThreadLocal. * The main purpose of using ThreadLocal to store the logoff corresponding to a thread is to ensure that * Each thread has only one unique logoff. ** Loop Code of the association between er and Its thread: * 1.3 logoff. mylogoff () * gets the logoff * 1.4 logoff associated with the current thread. mylogoff (). getThread () * Get the Thread associated with logoff ** 2 Handler handler = new Handler () * use logoff in step 1. prepare () implements the association between logoff and thread. * Next, let's look at the relationship between Handler and logoff. * pay attention to the Code in the preceding Handler constructor: * mLooper = Looper. mylogoff (); * in this way, the logoff associated with the thread is obtained. * mQueue = mloue. mQueue; * Get the MessageQueue Message Queue (MessageQueue) mQueue associated with the thread * as shown in the Handler construction method, Han Association between dler and logoff. ** conclusion: * (1) logoff. prepare (); * implements the association between logoff and the thread to which it belongs * (2) Handler handler = new Handler () * implements the association between Handler and logoff. * (3) one thread can have multiple Handler, but only one logoff *** ============================ =, here we will summarize the process of * Android asynchronous message mechanism: ** 1 logoff. prepare (); * implement logoff and its associated thread. * logoff contains three variables: * mQueue ----> MessageQueue * mRu N ------> true * mThread ---> the current thread * has a MessageQueue in each logoff after this method is executed. * All messages sent by Handler are stored in the MessageQueue. * 2 Handler handler = new Handler (); * associates Handler with logoff. * 3 logoff. loop (); * start to poll the Message Queue (MessageQueue) and keep polling. * process each message in the queue. of course, the queue is empty at the beginning. * 4 mHandler. sendMessage (message) sends a message to the message queue. * For detailed procedures, see the preceding description. * 5 logoff in step 3. isn't loop () always polling the message queue? * When a Message is out of the queue, find the target (actually a Handler) callback of the Message * its dispatchMessage (Message msg) method; in this method, handleMessage (Message msg) that we are very familiar with * is called ). ** the above is the detailed process of Android asynchronous message mechanism. * Simply put, you can say: * Handler ------> send messages and process messages * login-------> Use MessageQueue to save messages. use the loop () method to keep polling the message queue. * send the message to the appropriate Handler when it leaves the queue. ** for the above process analysis, see the flowchart. ** ===================================================== =========================== * References: * 1 http://androidxref.com/4.0.4/ * 2 http://blog.csdn.net/guolin_blog/article/details/9991569 * 3 http://blog.csdn.net/aaa2832/article/details/7773220 * 4 http://blog.csdn.net/lilu_leo/article/details/8145320 * 5 http://blog.csdn.net/oney139/article/details/7922742 * 6 http://www.cnblogs.com/cqcmdwym/archive/2013/05/12/3074138.html * Thank you very much */public class MainActivity extends Activity {private Thread mThread; private Handler mHandler; private final String TAG = "Handler"; @ Overrideprotected void onCreate (Bundle savedInstanceState) {super. onCreate (savedInstanceState); setContentView (R. layout. main); testHandler ();} private void testHandler () {mThread = new Thread (new Runnable () {@ Overridepublic void run () {loid. prepare (); mHandler = new Handler () {@ Overridepublic void handleMessage (Message msg) {super. handleMessage (msg); if (msg. what == 123) {Log. I (TAG, "received message msg. arg1 = "+ msg. arg1) ;}}; Message message = new Message (); message. what = 123; message. arg1 = 456; mHandler. sendMessage (message); logoff. loop () ;}}); mThread. start ();}}
The figure is as follows:
Main. xml is as follows: