preface

  • inAndroidThe development ofMulti-threaded application scenario,HandlerMechanics are very common
  • Today, I’m going to walk you through it hand in handHandlerMechanism source, I hope you will like

directory


1. Introduction to the Handler mechanism

  • Define an Android messaging mechanism

  • role

In a multithreaded application scenario,Update will be required in the worker threadUIThe operation information is passed toUIThe main threadTo implement worker thread pairsUIThe final realization of asynchronous message processing

  • Why use itHandlerMessaging mechanism

    A:Multiple threads update the UI concurrently while maintaining thread-safety. Detailed description is as follows

  • conclusion

    useHandlerCause: will the worker thread need to operateUIIs passed to the main thread so that the master thread can update as required by the worker threadUI.This avoids the problem of unsafe threading operations

2. Stock up on knowledge

Before reading the source code analysis of the Handler mechanism, it is important to understand the relevant concepts, how to use the Handler mechanism, and how it works

2.1 Related Concepts

The concepts related to the Handler mechanism are as follows:

I’ll use the names Handler, Message, Message Queue, and Looper to familiarize you with the concepts

2.2 Usage Mode



  • HandlerMode of useThe way messages are sent to message queues varies, is divided into two types: useHandler. SendMessage (), the use ofHandler. Post ()
  • The following source code analysis will be explained according to the usage steps

If not, be sure to read Android: This is a tutorial on how to use the Handler messaging mechanism

2.3 Working Principles

  • understandHandlerMechanism of the working principle, can be a great program to help understand its source
  • Android Handler: The Handler communication mechanism works

3. Core classes for the Handler mechanism

Before analyzing the source code, let’s look at the core classes in the Handler mechanism

3.1 class description

There are three important classes in the Handler mechanism:

  • The handler class(Handler)
  • Message queue class(MessageQueue)
  • Circulator class(which)

3.2 class diagram

3.3 Introduction


4. Source code analysis



  • The following source code analysis will be based onHandlerUse steps to proceed
  • HandlerMode of useThe way messages are sent to message queues varies, is divided into two types: useHandler. SendMessage (), the use ofHandler. Post ()

If not, be sure to read Android: This is a tutorial on how to use the Handler messaging mechanism

  • The following source code analysis will be based on the above two uses
  • Method 1: Use handler.sendMessage ()

    • Using the step
    /** * Step 1: Private Handler mHandler = new Handler(){// Override public to update the UI by overriding handlerMessage()  void handleMessage(Message msg) { ... // UI operations to perform}}; // Step 2: create the Message object Message MSG = message.obtain (); // instantiate the message object msg.what = 1; MSG. Obj = "AA"; // Multithreading can use AsyncTask, inherit Thread class, implement Runnable mHandler.sendMessage(MSG); // Multithreading can use AsyncTask, inherit Thread class, implement RunnableCopy the code
    • Below, I’ll analyze the source code for each of the above steps

    Step 1: Create a Handler class object from an anonymous inner class in the main thread

    // Private Handler mHandler = new Handler(){// Override public void Override handleMessage(Message msg) { ... // UI operations to perform}}; /** * Handler Handler Handler Handler Handler Handler Handler Handler Handler Handler Handler Handler Handler Handler Handler Handler Handler After binding, the Handler's message processing is performed in the bound thread * b. Binding = specify the Looper object first, thus binding the thread to which the Looper object is bound (because the Looper object is already bound to the corresponding thread) * c. Public Handler() {this(null, false); public Handler() {this(null, false); This (null, false) = Handler(null, false) */ public Handler(Callback Callback, Boolean async) {... // 1. Specify Looper object mLooper = looper.mylooper (); if (mLooper == null) { throw new RuntimeException( "Can't create handler inside thread that has not called Looper.prepare()"); } // looper.mylooper () returns the Looper object of the current thread; If the thread has no Looper object, the Handler object cannot be created. If the thread has no Looper object, the Handler object cannot be created. Loop.getmainlooper () can be used to obtain the main thread Looper object of the current process // 2. Bind MessageQueue object (MessageQueue) mQueue = mlooper.mqueue; MessageQueue} {// This handler is associated with MessageQueue}.Copy the code
    • When a Handler object is created, the constructor automatically associates the Looper object of the current thread with the corresponding MessageQueue object (MessageQueue), which automatically binds the thread that created the Handler object

    • So when was the Looper object for the current thread created and the corresponding MessageQueue object (MessageQueue) created?

      In the above steps, there is no Looper object & corresponding MessageQueue object (MessageQueue) step

    Implicit action 1 before Step 1: Create Looper object & MessageQueue

    • Steps to introduce

    • Source code analysis
    /** * Create a Looper object for the current thread (child thread) and a MessageQueue (MessageQueue). */ public static final void prepare() {if (sthreadLocal.get ()! = null) { throw new RuntimeException("Only one Looper may be created per thread"); } // 1. Check whether sThreadLocal is null, otherwise throw an exception // that is, looper.prepare () cannot be called twice = 1 Looper instance per thread // SThreadLocal = 1 ThreadLocal object, used to store the thread variable sthreadLocal. set(new Looper(true)); Create a Looper object and store it in a ThreadLocal variable. Looper object is stored in the Thread Looper constructor ->> analyze a} /** * analyze a: **/ private Looper(Boolean quitAllowed) {mQueue = new MessageQueue(quitAllowed); MessageQueue mRun = true; MessageQueue mRun = true; mThread = Thread.currentThread(); PrepareMainLooper () creates a Looper object for the main thread (UI thread) and a MessageQueue (MessageQueue). This method is automatically called when the main thread (UI thread) is created, that is, the main thread Looper object is automatically generated, no need to manually generate */ / When the Android application starts, the default main thread (ActivityThread) is created. PrepareMainLooper () generates a Looper object for the main thread. ** * Main () **/ public static void main(String[] args) {... PrepareMainLooper (); prepareMainLooper(); Create a MessageQueue object (MessageQueue) for the main thread. ActivityThread thread = new ActivityThread(); // 2. Create main thread looper.loop (); // 3. Automatically start the message loop ->> Details below}Copy the code

    Conclusion:



    • Automatically called when the main thread is createdActivityThreadOne of the staticThe main (); whileThe main ()The inner will callLooper.prepareMainLooper()Generates 1 for the master threadLooperObject, which also generates its correspondingMessageQueueobject

    1. That’s the main threadLooperObject automatically generated, no need to manually generated; And the child threadLooperObjects must be manually passedLooper.prepare()create

    2. If the child thread is not created manuallyLooperObject cannot be generatedHandlerobject
    3. Handler is used to update the UI in the main thread, so Handler instances are created in the main thread

    4. Once the Looper & MessageQueue object is generated, the message loop is automatically entered: looper.loop (), which is yet another implicit operation.

    Implicit action 2 before Step 1: message loop

    The focus here is on the loop () method of the Looper class

    /** ** looper.loop () * uses a message loop to retrieve a message from a message queue and distribute the message to a Handler. MessageQueue quit () */ public static void loop() {... Final Looper me = myLooper(); final Looper me = myLooper(); if (me == null) { throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread."); } // myLooper() returns the Looper instance stored in sThreadLocal; If me is null, an exception is raised. Prepare () must be executed before loop () to create a Looper instance. // get the MessageQueue object in Looper instance (MessageQueue) // 2. Message loop (through the for loop) for (;;) Message MSG = queue.next(); if (msg == null) { return; } / / next () : if the message in the message queue / / took the message is null, the thread blocking / / - > > analysis 1 / / 2.2 distributed message to the corresponding Handler MSG. Target. DispatchMessage (MSG); // The target attribute is actually a handler object // ->> analyze 2 // 3. MSG. Recycle (); }} /** * Analysis 1: queue.next() * Definition: a method that belongs to MessageQueue */ Message next() {... NextPollTimeoutMillis = 0; nextPollTimeoutMillis = 0; for (;;) { if (nextPollTimeoutMillis ! = 0) { Binder.flushPendingCommands(); NativePollOnce (PTR, nextPollTimeoutMillis);} // If nextPollTimeoutMillis is -1, the message queue is in the waiting state. synchronized (this) { final long now = SystemClock.uptimeMillis(); Message prevMsg = null; Message msg = mMessages; If (MSG!) if (MSG!) if (MSG! = null) { if (now < msg.when) { nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE); } else {// Message mBlocked = false; if (prevMsg ! = null) { prevMsg.next = msg.next; } else { mMessages = msg.next; } msg.next = null; if (DEBUG) Log.v(TAG, "Returning message: " + msg); msg.markInUse(); return msg; NextPollTimeoutMillis = -1; nextPollTimeoutMillis = -1; nextPollTimeoutMillis = -1; nextPollTimeoutMillis = -1; nextPollTimeoutMillis = -1; }... }... }}/ ** * dispatchMessage(MSG); */ public void dispatchMessage(Message MSG) {// 1. If msg.callback is not empty, it means post (Runnable r) was used to send the message. If (msg.callback!) if (msg.callback!) if (msg.callback! = null) { handleCallback(msg); } else { if (mCallback ! = null) { if (mCallback.handleMessage(msg)) { return; }} // 2. If msg.callback is empty, sendMessage (MSG) is used to send the Message(which is discussed here) // handleMessage(MSG) is executed. ->> Parse handleMessage(MSG); }} /** * Public void handleMessage(Message MSG) {public void handleMessage(Message MSG) {... // create Handler instance overwrite}Copy the code

    Conclusion:

    • Operation of message loop = message out queue + dispatch to correspondingHandlerThe instance
    • Distribute to the correspondingHandlerProcess: pass according to the owner of the outgoing team messagedispatchMessage(msg)Distribute, and finally write backhandleMessage(Message msg), so as to realize the operation of message processing
    • Special note: during message distribution(dispatchMessage (MSG)), will determine the mode of sending once:

      1. ifmsg.callbackProperty is not empty, indicating usePost (Runnable r)If a message is sent, the callback is made directlyRunnableCarbon copy of the objectThe run ()
      2. ifmsg.callbackProperty is null, indicating that it is usedSendMessage (Message MSG)Send message, then callback overwrittenhandleMessage(msg)

    At this point, the source code analysis of step 1 is explained. Summarized below


    Step 2: Create the message object

    /** * obtain(); // instantiate the message object msg.what = 1; MSG. Obj = "AA"; Message.obtain() message.obtain () Message.obtain() */ public static Message obtain() { Reuse // Using obtain () for Message Message objects obtains synchronized (sPoolSync) {if (sPool! = null) { Message m = sPool; sPool = m.next; m.next = null; m.flags = 0; // clear in-use flag sPoolSize--; return m; } // Suggestion: use obtain () to "create" Message objects and avoid reallocating memory using new each time} // If there are no Message objects to reuse, use the keyword new to create return new Message(); }Copy the code
    • conclusion

    Step 3: Send messages in the worker thread to the message queue

    The implementation of multithreading: AsyncTask, Thread class inheritance, implementation Runnable

    /* / mhandler.sendMessage (MSG); /** * mhandler.sendMessage (MSG) SendMessage to Message queue (Message ->> MessageQueue) */ public final Boolean sendMessage(Message MSG) {return sendMessageDelayed(msg, 0); } /** * 1: sendMessageDelayed(msg, 0) **/ public final boolean sendMessageDelayed(Message msg, long delayMillis) { if (delayMillis < 0) { delayMillis = 0; } return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis); // ->> analysis 2} ** * analysis 2: sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis) **/ public boolean sendMessageAtTime(Message msg, long uptimeMillis) { // 1. MessageQueue MessageQueue queue = mQueue; Return enqueueMessage(queue, MSG, uptimeMillis); } /** * analysis 3: enqueueMessage(queue, msg, uptimeMillis) **/ private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) { // 1. MSG. Target = this; MSG. Target = this; // Recall that the message loop in Looper's loop() fetches each message MSG from the message queue, Then execute MSG. Target. DispatchMessage (MSG) to process the message / / in fact is to send the message to send to the corresponding Handler instance / / 2. 4 return queuemessage (MSG, uptimeMillis); } /** * Analysis 4: queue.enqueuemessage (MSG, uptimeMillis) * Definition: a method belonging to the MessageQueue class * Enqueueing, i.e. putting a Message into a Message queue according to the time (Message ->> MessageQueue) * Single linked list implementation: Boolean enqueueMessage(Message MSG, long WHEN) {... Synchronized (this) {msg.markinuse (); synchronized (this); msg.when = when; Message p = mMessages; boolean needWake; / / determine any message in the message queue / / a. if not, insert the current message As a team if head & message queues in a wait state at this time, fire up the if (p = = null | | the when = = 0 | | the when < p.w hen) {MSG. Next = p; mMessages = msg; needWake = mBlocked; } else { needWake = mBlocked && p.target == null && msg.isAsynchronous(); Message prev; For (;;); { prev = p; p = p.next; if (p == null || when < p.when) { break; } if (needWake && p.isAsynchronous()) { needWake = false; } } msg.next = p; prev.next = msg; } if (needWake) { nativeWake(mPtr); } } return true; } // After that, as the Looper object's infinite message loop // continues to fetch messages sent by the Handler from the message queue & distribute them to the corresponding Handler // Finally call handler.handleMessage () to process the messageCopy the code
    • conclusion

      HandlerThe nature of the sent message = defined for the messagetargetProperty (itself instance object) & to enqueue messages to the message queue of the bound thread. Details are as follows:

    At this point, the source code for using handler.sendMessage () is parsed

    conclusion

    • Source code analysis and summary according to operation steps

    • Summary of workflow

    Next, I will follow the article: Workflow again


    Approach 2: Use handler.post ()

    • Using the step
    Private Handler mHandler = new mHandler(); Mhandler. post(new Runnable() {@override public void run() {... mhandler. post(new Runnable() {@override public void run() {... // UI operations to perform}}); // Multithreading can use AsyncTask, inherit Thread class, implement RunnableCopy the code
    • Source code analysis

      Below, I’ll analyze the source code for each of the above steps


      In fact, this mode is different from that in mode 1
      Handler. SendMessage ()The working principle is the same, the source code analysis is similar, the following will mainly explain the differences

    Step 1: Create Handler instances in the main thread

    Private Handler mhandler = new Handler(); HandleMessage (); handleMessage(); ** * Previously, main thread creation implicitly created Looper objects, MessageQueue objects * b. Initialize the Handler object, bind the thread & enter the message loop * The source code analysis here is similar to 1, which is not described here too much */Copy the code

    Step 2: Send messages in the worker thread to the message queue

    / / mhandler.post (new Runnable() {@override public void run() {/ / mhandler.post (new Runnable() {@override public void run() {... // UI operations to perform}}); Handler. Post (Runnable r) * Defines a method that belongs to the Handler class. Define UI operations, encapsulate Runnable objects as Message objects & send them to Message queues (Message ->> MessageQueue) The big difference between post () and sendMessage() is that the updated UI operation can be defined directly in the overridden run (). In fact, Runnable does not create a new thread, */ Public Final Boolean POST (Runnable r) {return sendMessageDelayed(getPostMessage(r), 0); GetPostMessage (r) = getPostMessage(r) = getPostMessage(r) = getPostMessage(r) = getPostMessage(r) = getPostMessage(r) Private static Message getPostMessage(Runnable r) {// 1. Create a Message object (Message) Message m = Message.obtain(); Message.obtain(); // Note: Create a Message object using the keyword new or message.obtain () Because a Message pool is maintained internally for Message reuse, use obtain () to obtain memory directly from the pool, avoiding the need to reallocate memory using new. Assign the Runable object to the message object's callback property m.callback = r; // 3. Return m; } // return to the original call /** * In fact, from this point on, messages are queued in a similar way 1 = to a message queue, * that is ultimately call MessageQueue. EnqueueMessage () * * / public final Boolean sendMessageDelayed (Message MSG, long delayMillis) { if (delayMillis < 0) { delayMillis = 0; } return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis); } /** * sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis) **/ public boolean sendMessageAtTime(Message msg, long uptimeMillis) { // 1. MessageQueue MessageQueue queue = mQueue; Return enqueueMessage(queue, MSG, uptimeMillis); } /** * analysis 4: enqueueMessage(queue, msg, uptimeMillis) **/ private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) { // 1. MSG. Target = this; MSG. Target = this; // Recall that the message loop in Looper's loop() fetches each message MSG from the message queue, Then execute MSG. Target. DispatchMessage (MSG) to process the message / / in fact is to send the message to send to the corresponding Handler instance / / 2. Return queuemessage (MSG, uptimeMillis); return queuemessage (MSG, uptimeMillis); } // Note: Actually from analysis 2, the source code is the same as sendMessage (Message MSG)Copy the code

    As can be seen from the above analysis:

    1. Creation of message objects = internal basisRunnableObject encapsulation
    2. Send to message queue logic = method 1SendMessage (Message MSG)

    Next, we return to implicit operation 2 before Step 1: the message loop, the loop () method of the Looper class

    /** ** looper.loop () * uses a message loop to retrieve a message from a message queue and distribute the message to a Handler. MessageQueue quit () */ public static void loop() {... Final Looper me = myLooper(); final Looper me = myLooper(); if (me == null) { throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread."); } // myLooper() returns the Looper instance stored in sThreadLocal; If me is null, an exception is raised. Prepare () must be executed before loop () to create a Looper instance. // get the MessageQueue object in Looper instance (MessageQueue) // 2. Message loop (through the for loop) for (;;) Message MSG = queue.next(); if (msg == null) { return; } / / next () : if the message in the message queue / / took the message is empty, the thread block / 2.2 / distributing information to the corresponding Handler MSG. Target. DispatchMessage (MSG); // The target attribute is actually a handler object // ->> analysis 1 // 3. MSG. Recycle (); }} /** * dispatchMessage(MSG) * */ public void dispatchMessage(Message MSG) {// 1. If msg.callback is not empty, then post (Runnable r) was used to send the message (which is discussed here) // then handleCallback(MSG) is executed, 2 if (msg.callback! = null) { handleCallback(msg); } else { if (mCallback ! = null) { if (mCallback.handleMessage(msg)) { return; }} // 2. If msg.callback is empty, sendMessage (MSG) is used to send the Message(which is discussed here) // handleMessage(MSG) is executed. That's the handleMessage(MSG) that the callback overwrites; Private static void handleCallback(Message Message) {message.callback.run(); // Message callback = Runnable; // Run ()}Copy the code

    At this point, you should understand the workflow using handler.post () : it is similar to method 1 (handler.sendMessage ()), except that:

    1. Message objects are not created externally, but internally based on incoming messagesRunnableObject encapsulates the message object
    2. The message handling method for the callback is: overrideRunnableThe object’sThe run ()

    The specific similarities and differences are as follows:

    At this point, the source code for using handler.post () is parsed

    conclusion

    • Source code analysis and summary according to operation steps

    • Summary of workflow

    Next, I will follow the article: Workflow again

    So far, with respect toHandlerMechanism of all the source code analysis completed.


    5. To summarize

    • This paper analyzes in detailHandlerMechanism source, text summary & flow chart as follows:

    • I’m going to go into more detailAndroidIn theHandlerRelated knowledge of asynchronous communication transfer mechanism, such as working mechanism flow, source code analysis, etc., interested in you can continue to pay attention toCarson_Ho android Development Notes

    Please give the top/comment a thumbs up! Because your encouragement is the biggest power that I write!