Android asynchronous message processing mechanism looper, Handler, message three relationships

Source: Internet
Author: User

Looper,handler,message is what we often use to re-thread the new UI, we send the message to Handler, and then, Handler calls the Handlermessage () method, we update the UI in this method. So looper, what is it, let me introduce you to the three relations.

The first thing to note is that there is at most one looper per thread, and the thread inside calls Looper.prepare () is to set up a looper for it, so we must call the Looper.prepare () method before the child thread, we use this three. And the main thread has actually called this method for us, so we don't have to repeat the call.

In addition, we also have to invoke the Looper.loop () method, the following to explain why.

Looper contains a MessageQueue, this is a message queue, handler sent message messages will come to this queue, looper.loop () constantly remove the message from the MessageQueue, no block, There is a call to Message.target's DispatchMessage (msg) method, and this method of message is passed over. The DispatchMessage (msg) method calls the Handlemessage () method, and this method is our concrete implementation.

So it is clear from the description just now that Message.target is actually handler.


OK, then I'll summarize the citation relationship between the three.

Looper: Has a MessageQueue that contains all the message

Message: Each message holds a handler reference

Handler:handler holds Looper

That is, any one can get the other two references, thus associating with each other.


The previous may not be very good expression, the following through the source to explain

First look at the static method of Looper prepare ()

public static void Prepare () {        if (sthreadlocal.get () = null) {            throw new RuntimeException ("Only one Looper may b E created per thread ");        }        Sthreadlocal.set (New Looper ());    }
This method is what we have to invoke voluntarily, Sthreadlocal is a class provided by a Java class library for maintaining the same variable in different threads. This is more difficult to explain, that is, a variable, it should have been shared by multiple threads, so when we multi-threaded operation, to consider the synchronization problem, but after using threadlocal, the system allocates a space for each thread to store the variable, In other words, each thread can only maintain its own variables, and this variable is not shared, thus avoiding the problem of synchronization.

As we can see, prepare () is only required to have a Looper instance through the Sthreadlocal.get () method, and if duplicate prepare () throws an exception (like a singleton pattern, the difference is that the result of a duplicate creation is to throw an exception).

Then, if there is no looper instance, go to new one and save it in sthreadlocal.

See the new Looper () method below

Private Looper () {        mqueue = new MessageQueue ();        Mrun = true;        Mthread = Thread.CurrentThread ();    }
You can see that this construction method is private (so like a singleton mode), and creates a message queue mqueue, and also holds a reference to the thread that created the Looper (each thread has only one looper)

Prepare (), we're going to call the static loop () method and get the message out of the Mqueue message queue.

See the Loop () method below

public static void Loop () {Looper me = Mylooper (); if (me = = null) {throw new RuntimeException ("No Looper;        Looper.prepare () wasn ' t called on this thread. ");                MessageQueue queue = Me.mqueue; Make sure the identity of the the the the The local process,//and keep track of what the identity toke        n actually is.        Binder.clearcallingidentity ();                Final Long ident = Binder.clearcallingidentity (); while (true) {Message msg = Queue.next ();//might block if (msg! = null) {if (msg.                    target = = null) {//No target is a magic identifier for the quit message.                Return                } long Wallstart = 0;                Long ThreadStart = 0;                This must is in a local variable with case a UI event sets the logger Printer logging = me.mlogging;    if (logging! = null) {                Logging.println (">>>>> dispatching to" + Msg.target + "" + msg                    . Callback + ":" + msg.what);                    Wallstart = Systemclock.currenttimemicro ();                ThreadStart = Systemclock.currentthreadtimemicro ();                } msg.target.dispatchMessage (msg);                    if (logging! = null) {Long walltime = Systemclock.currenttimemicro ()-Wallstart;                    Long threadtime = Systemclock.currentthreadtimemicro ()-ThreadStart;                    Logging.println ("<<<<< finished to" + Msg.target + "+ msg.callback);                                If (Logging instanceof Profiler) {(profiler) logging). Profile (msg, Wallstart, Walltime,                    ThreadStart, ThreadTime);  }}//Make sure that during the course of dispatching the//identity of the ThreaD wasn ' t corrupted.                Final Long newident = Binder.clearcallingidentity (); if (ident! = newident) {LOG.WTF (TAG, "Thread identity changed from 0x" + Lo                            Ng.tohexstring (ident) + "to 0x" + long.tohexstring (newident) + "when dispatching to" + Msg.target.getClass (). GetName () + "" + Msg.callback + "what=" + MSG.WH                at);            } msg.recycle (); }        }    }
The Mylooper () method is a get method used to get the Looper object, and the first few lines of code indicate that there is no prepare () and that the loop () cannot be called

The call then enters a dead loop, and Queue.next () constantly gets the message object from the queue, and once it gets to messgae, it calls the Msg.target.dispatchMessage (msg) method.

Ok,loop () Basically did these things, so there were a few questions about when the message was added to the queue, and then what was the Msg.target object, and what did the DispatchMessage (msg) method do?


Next we look at handler object source code, just look to solve these problems.

The first is its constructor

Public Handler () {        if (find_potential_leaks) {            final class<? extends handler> Klass = GetClass ();            if (Klass.isanonymousclass () | | klass.ismemberclass () | | klass.islocalclass ()) &&                    (Klass.getmodifiers () & modifier.static) = = 0) {                log.w (TAG, "the following Handler class should be STATIC or leaks might occur:" +                    Klass.getcanonicalname ());            }        }        Mlooper = Looper.mylooper ();        if (Mlooper = = null) {            throw new RuntimeException (                "Can ' t create handler inside thread that have not called Loope R.prepare () ");        }        Mqueue = Mlooper.mqueue;        Mcallback = null;    }

As you can see from the constructor, handler gets a reference to the Looper object and gets a reference to the MQEUEU message queue

So we add msg to the queue, generally we call handler's SendMessage () method to send the message, but these methods will call the same method at the end, Sendmessageattime

public boolean sendmessageattime (Message msg, long Uptimemillis)    {        Boolean sent = false;        MessageQueue queue = Mqueue;        if (queue! = null) {            msg.target = this;            Sent = Queue.enqueuemessage (msg, uptimemillis);        }        else {            RuntimeException e = new RuntimeException (this                + "sendmessageattime () called with no Mqueue");            LOG.W ("Looper", E.getmessage (), E);        }        return sent;    }

In this method we can see that we set the target of MSG to handler itself, and then add MSG to the queue message queues.

It turns out we put MSG in the message queue when we sendmessage.


Finally, let's look at the loop () method

Msg.target.dispatchMessage (msg);
That is to call the DispatchMessage () method of handler, so generally, handler put msg into the queue, loop constantly remove MSG, and then call handler DispatchMessage () method to process Msg

Then look at the DispatchMessage () method

public void DispatchMessage (Message msg) {        if (msg.callback! = null) {            handlecallback (msg);        } else {            if ( Mcallback! = null) {                if (Mcallback.handlemessage (msg)) {                    return;                }            }            Handlemessage (msg);        }    }

In this method, we call the Handlemessage method, which is actually a callback, inside of this method, I write the processing of the MSG myself


From the above source summary can basically clarify the relationship between Looper, Handler, message three relations, and then mention the UI in the sub-thread operation of other methods:

1. Handler's post () method

2. View's post () method

3. Runonuithread () Method of activity

Let's take a look at the post () method in handler, where the code looks like this:
Public Final Boolean post (Runnable R)    {       return  sendmessagedelayed (Getpostmessage (R), 0);    }

Originally here still called the sendmessagedelayed () method to send a message ah, and also used the Getpostmessage () method to convert the Runnable object into a message, we look at the source of this method:

Private final Message Getpostmessage (Runnable r) {    Message m = Message.obtain ();    M.callback = R;    return m;}

Note that here callback is set to Runnable object, remember our DispatchMessage () method?

public void DispatchMessage (Message msg) {        if (msg.callback! = null) {            handlecallback (msg);        } else {            if ( Mcallback! = null) {                if (Mcallback.handlemessage (msg)) {                    return;                }            }            Handlemessage (msg);        }    }
Inside the first to determine whether the callback is empty, not empty, to call Handlecallback ()

Private final void Handlecallback (Message message) {        message.callback.run ();    }
The runnable's Run () method is invoked here, so this is not an operation to turn on the sub-threading! (Note that the active call to the run () method does not open the thread)


Then look at the post () method in view, as shown in the code below:

public Boolean post (Runnable action) {    Handler Handler;    if (mattachinfo! = null) {        handler = Mattachinfo.mhandler;    } else {        viewroot.getrunqueue (). Post (action);        return true;    }    Return Handler.post (action);}

It turns out that the post () method in handler is called

Finally, take a look at the Runonuithread () method in activity, as shown in the code below:

Public final void Runonuithread (Runnable action) {    if (thread.currentthread ()! = Muithread) {        mhandler.post ( action);    } else {        action.run ();    }}

If the current thread is not equal to the UI thread (the main thread), call handler's post () method, or call the Runnable object's run () method directly.

Android asynchronous message processing mechanism looper, Handler, message three relationships

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.