10 minutes to learn about Android's handler mechanism

Source: Internet
Author: User

The handler mechanism is a fairly classic asynchronous messaging mechanism in Android that plays an important role in the history of Android, and is used in many scenarios, whether we are directly facing the application layer or the framework layer. Analysis of the source of a probe. From a common usage, say:

Private Button mbtntest;Private Handler Mtesthandler =New Handler () {@OverridePublicvoidHandlemessage(Message msg) {Switch (msg.what) {Case1:mbtntest.settext ("Received message 1"); } }};@Overrideprotected void onCreate (final Bundle savedinstancestate) { Super.oncreate (savedinstancestate); Setcontentview (R.layout.activity_main); Mbtntest = (Button) Findviewbyid (r.id.btn_test); new Thread (new Runnable () {@ Override public void run () {try {thread.sleep ( 3000); Mtesthandler.sendemptymessage (1);} catch (interruptedexception e) {e.printstacktrace ();}} }). Start ();}                

Before we learn more about something, we must first understand the value of the thing, that is, what he does, what it does when it occurs or what happens, and what the end result will be. What we're going to talk about is what's going on in this process, what's happening, and what's going to happen.

Where will the message be sent when calling handler to send a message-related method? From the example code above you can see that the message will eventually go back to the handler hand and be handled by himself. What we need to figure out is the process of sending the message to the receiving part.

Where will the message be sent?

Mtesthandler.sendemptymessage (1);

We follow the Sendemptymessage () method down:

Handler regardless of the way the message is sent, it goes through the Sendmessageattime () method:

 public boolean sendmessageattimelong UptimeMillis {MessageQueue queue = Mqueue; if (queue = = null) {runtimeexception e = new runtimeexception (this + " SendMessageAtTime () called with no Mqueue "); LOG.W ( "Looper", E.getmessage (), E); return FALSE;} return enqueuemessage (queue, MSG, uptimemillis);}   

This method first determines whether the current handler Mqueue object is empty, and then calls the Enqueuemessage () method, which is literally not difficult to understand is to queue the message for saving. Look again at the Enqueuemessage () method:

 private  Boolean enqueuemessage (MessageQueue queue, Message msg, long uptimemillis) {msg.target = this; if (masynchronous) {msg.setasynchronous (true);} return queue.enqueuemessage (msg, uptimemillis);} public final  class message implements parcelable {//.... Handler target;}                

This method binds the message to the current handler and is not difficult to understand when it is necessary to dispose of the message directly to the handler that binds him. Then call the Queue.enqueuemessage () method to formally join the queue, and what kind of object is the object? Message Queuing implemented by a one-way linked list. The Queue.enqueuemessage () method is to iterate through the list to save the message at the end of the table, and the message from the queue is to take out the header.

And then we'll figure out when the queue was created. See the handler constructor.

public Handler(Callback callback, boolean async) { mLooper = Looper.myLooper(); if (mLooper == null) { throw new RuntimeException( "Can‘t create handler inside thread that has not called Looper.prepare()"); } mQueue = mLooper.mQueue; mCallback = callback; mAsynchronous = async;}

The handler method calls the Looper.mylooper () method first to see if it can get a Looper object, and jumps if the program is not available. Then get the message queue we need from the Looper object.

What kind of object is looper, and what kind of identity is there, and what role does it play in the handler mechanism? See Mylooper () Method:

public static @Nullable Looper myLooper() { return sThreadLocal.get();}

The Mylooper () method takes the Looper directly from the Sthreadlocal object, and Sthreadlocal is a threadlocal class object, and the Threadlocal class is plainly the object that is stored by him is thread-private.

Static final threadlocal<looper> sthreadlocal = new threadlocal<looper> ();

Calling the Get () method takes looper directly from Threadlocal, and then it depends on when set () saves the Loooper object to threadlocal. Looper.prepare () Method:

 private  Static void prepare (boolean quitallowed) {if (Sthreadlocal.get ()! = null) {throw new runtimeexception ( " Looper may created per thread "); } sthreadlocal.set (new Looper (quitallowed));} private Looper  (boolean quitallowed) {mqueue =  New MessageQueue (quitallowed); Mthread = Thread.CurrentThread ();}             

From this source can be seen, Looper is not only the thread private or the only non-replaceable. The Looper object is created to initialize the MessageQueue () object, which is exactly the queue we need.
We did not call the prepare () method to initialize the Looper in the top example code, and the program did not crash because the Looper object was initialized in the main method of Activitythread.

public final class ActivityThread { //...... public static void main(String[] args) { Looper.prepareMainLooper(); } //......}

Here we understand where the message will be sent, and now we need to know how to take out the message to handler processing.

First, the MessageQueue package has a complete add (queue) and get/delete (out of line) method, the Messagequeeue.next () method takes the first message of the table header in the list out.

MessageNext() {//..........for (;;) {if (nextpolltimeoutmillis! =0) {binder.flushpendingcommands ();} nativepollonce (PTR, nextpolltimeoutmillis);Synchronized (This) {FinalLong now = Systemclock.uptimemillis (); Message prevmsg =Null Message msg = mmessages;if (msg! =Null && Msg.target = =NULL) {do {prevmsg = msg; msg = Msg.next;}while (msg! =Null &&!msg.isasynchronous ()); }if (msg! =NULL) {if (now < Msg.when) {Nextpolltimeoutmillis = (int) Math.min (Msg.when-now, Integer.max_value); } else {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;} } else {nextpolltimeoutmillis =-1;} if (mquitting) {Dispose (); return null;} //..... ...} //...}}           (...) 

Although the code is much more, we start with the third and 39th lines. The next () method is actually a dead loop that will always fetch a message from the current queue, even if the current queue does not have a message, does not jump out of the loop, and executes until it is able to fetch the message from the queue. The next () method executes the end. Second, when Looper calls the Quit () method, the mquitting variable jumps out of the loop when it is ture, and the next () method returns the Null method and executes the end. As mentioned above, the Looper is initialized in the main () method in Activitythread, which in fact will begin to fetch messages from the queue shortly thereafter.

Publicstatic void main //... Looper.preparemainlooper (); Activitythread thread = new activitythread (); Thread.attach ( FALSE); if (Smainthreadhandler = null) {Smainthreadhandler = Thread.gethandler (); } if (false) {Looper.mylooper (). setmessagelogging (new logprinter (log.debug,  "Activitythread")); } trace.traceend (Trace.trace_tag_activity_manager); Looper.loop (); throw new runtimeexception ( "Main Thread loop unexpectedly exited ")}             

Calling the Looper.loop () method starts traversing the fetch message.

PublicStaticvoidLoop() {for (;;) {Message msg = Queue.next ();Might blockif (msg = =NULL) {Return }Final Printer logging = me.mlogging;if (Logging! =NULL) {LOGGING.PRINTLN (">>>>> dispatching to" + Msg.target +"" + Msg.callback +":" + Msg.what);}FinalLong Slowdispatchthresholdms = ME.MSLOWDISPATCHTHRESHOLDMS;final long tracetag = Me.mtracetag; if (tracetag! = 0 && trace.istagenabled (TraceTag)) { Trace.tracebegin (Tracetag, Msg.target.getTraceName (msg)); } final long start = (Slowdispatchthresholdms = = 0)? 0:systemclock.uptimemillis (); final long end; try {msg.target.dispatchMessage (msg); end = (Slowdispatchthresholdms = = 0)? 0:systemclock.uptimemillis (); finally {if (tracetag! = 0) { Trace.traceend (Tracetag); } }}

The loop () method is also a dead loop, calling the Queue.nex () method to start a blocking fetch message, and the next () method will return null only if you manually let Looper stop.

After the message is taken, the DispatchMessage () method is called to send the message to handler for processing.

Msg.target.dispatchMessage (msg);

public void dispatchMessage(Message msg) { if (msg.callback != null) { handleCallback(msg); } else { if (mCallback != null) { if (mCallback.handleMessage(msg)) { return; } } handleMessage(msg); }}

You can not only set the callback interface for handler, but also the message line. The Handlemessage () method is callback by default.

I thought I was talking about it, but there was a key problem. We are the loop () method executed in the main thread, why did the dead loop not cause the activity to block the card to die? Check the data why the main thread in Android does not know that the next () method will perform an important method because of the death of the Looper.loop () in the dead loop card.

Nativepollonce (PTR, nextpolltimeoutmillis);

The big guy analyzed very well, I didn't say much. To put it another way, the delay message we send will be saved by the Message field/variable when, and the delay is done by this method.

Message next() {    final long now = SystemClock.uptimeMillis(); if (msg != null) { if (now < msg.when) { // Next message is not ready. Set a timeout to wake up when it is ready. nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE); } else { //...... } }}

In summary, Handler sends a message to the Looper maintained message Queue MessageQueue, while the Looper loop continues to fetch messages from the queue, which is processed by the handler callback that is bound to the message when it is taken.

If you have any questions, please contact me. Public Number: Terminal Research and Development department


Technology

10 minutes to learn about Android's handler mechanism

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.