The Android asynchronous message processing mechanism gives you a deep understanding of Looper, Handler, and message relationships

Source: Internet
Author: User
Tags getmessage

A lot of people interviewed must have been asked, what is the relationship between Looper, Handler, and message in Android? The purpose of this blog is to introduce 3 relationships from the source point of view, and then give a conclusion that is easy to remember.
1. Overview handler, Looper, and message all three concepts related to the Android asynchronous message processing thread. So what is the asynchronous message processing thread?
When the asynchronous message processing thread starts, it enters an infinite loop body, takes a message out of its internal message queue once per loop, and then callbacks the corresponding message handler, and then resumes the loop after the completion of a message. If the message queue is empty, the thread will block the wait.

Having said this heap, what does it have to do with handler, Looper, and message? In fact, Looper is responsible for creating a MessageQueue, and then entering an infinite loop to continuously read the message from the MessageQueue, and the creator of the message is one or more handler.

2, source code Analysis 1, Looper for Looper is mainly prepare () and loop () two methods.
First look at the prepare () method
public static final void prepare () {        if (sthreadlocal.get () = null) {            throw new RuntimeException ("Only one Looper May is created per thread ");        }        Sthreadlocal.set (New Looper (true));

Sthreadlocal is a Threadlocal object that can store variables in a thread. As you can see, in line 5th, an instance of Looper is put into threadlocal, and 2-4 rows determine whether sthreadlocal is null or throws an exception. This also shows that the Looper.prepare () method cannot be called two times and also guarantees that there is only one looper instance in a thread ~ I believe some of my buddies must have encountered this error.
Here's how to construct the Looper:
Private Looper (Boolean quitallowed) {        mqueue = new MessageQueue (quitallowed);        Mrun = true;        Mthread = Thread.CurrentThread ();}
In the construction method, a MessageQueue (message queue) is created.
Then we look at the loop () method:
public static void Loop () {final Looper me = Mylooper (); if (me = = null) {throw new RuntimeException ("No Looper;        Looper.prepare () wasn ' t called on this thread. ");        Final 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 (); for (;;) {Message msg = Queue.next ();//might block if (msg = = NULL) {//No Message Indicat                ES the message queue is quitting.            Return }//This must is in a local variable, in case a UI event sets the logger Printer logging = Me.mlogg            ing                        if (logging! = null) {logging.println (">>>>> dispatching to" + Msg.target + "" + Msg.callback + ":" + msg.what);            } msg.target.dispatchMessage (msg); if (logging! = null) {logging.println ("<<<<< finished to" + Msg.target + "" + Msg.callbac            k);             }//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" + Long.tohex                        String (ident) + "to 0x" + long.tohexstring (newident) + "when dispatching to"            + Msg.target.getClass (). GetName () + "" + Msg.callback + "what=" + msg.what);        } msg.recycle (); }}

Line 2nd:
public static Looper Mylooper () {
return Sthreadlocal.get ();
}
The Sthreadlocal method directly returns the Looper instance of the store, and throws an exception if me is null, meaning that the Looper method must run after the Prepare method.
Line 6th: Get the Mqueue (Message Queuing) in the Looper instance
13 to 45 lines: it goes into what we call an infinite loop.
14 rows: Takes out a message and blocks if no message is heard.
27 Line: Use Call Msg.target.dispatchMessage (msg), the message to the MSG's target DispatchMessage method to deal with. What is the target of MSG? is actually the handler object, the following will be analyzed.
44 Rows: Releases the resources that the message occupies.

Looper main functions:
1, bound to the current thread, to ensure that a thread will have only one looper instance, while a Looper instance has only one MessageQueue.
2, Loop () method, constantly from MessageQueue to fetch messages, to the message of the target property of the dispatchmessage to deal with.
Well, our asynchronous message processing thread already has Message Queuing (MessageQueue), and the guy who pulls the message out of the infinite loop is now missing the object that sent the message, so: handler.

2, handler before using handler, we are initializing an instance, for example, to update the UI thread, we will initialize it directly at the time of declaration, or initialize the handler instance in OnCreate. So we first look at how handler is structured, how it relates to MessageQueue, and how it sends messages in a child thread (typically sending messages in a non-UI thread) to MessageQueue.
Public Handler () {This        (null, FALSE);} Public Handler (Callback Callback, Boolean async) {        if (find_potential_leaks) {            final class<? extends handler& Gt 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 = callback;        masynchronous = async;    }

14 rows: The Looper instance saved by the current thread is obtained through Looper.mylooper (), and then the MessageQueue (Message queue) saved in this Looper instance is obtained in 19 rows. This ensures that the handler instance is associated with the MessageQueue in our Looper instance.

And see our most common SendMessage method.

   Public Final Boolean sendMessage (Message msg)    {        return sendmessagedelayed (msg, 0);    }

   Public final Boolean sendemptymessagedelayed (int what, long Delaymillis) {        Message msg = Message.obtain ();        Msg.what = what;        Return sendmessagedelayed (msg, delaymillis);    }

Public Final Boolean sendmessagedelayed (Message msg, long Delaymillis)    {        if (Delaymillis < 0) {            Delaymillis = 0;        }        Return Sendmessageattime (MSG, systemclock.uptimemillis () + Delaymillis);    }

public boolean sendmessageattime (Message msg, long 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);    }

The sendmessageattime is finally called, and in this method there is a direct fetch of MessageQueue and then the Enqueuemessage method is called, so let's take a look at this method:

Private Boolean enqueuemessage (MessageQueue queue, Message msg, long Uptimemillis) {        msg.target = this;        if (masynchronous) {            msg.setasynchronous (true);        }        Return Queue.enqueuemessage (msg, uptimemillis);    }

Enqueuemessage first assigns the meg.target to this, "If you remember Looper's Loop method will take each MSG and then give Msg,target.dispatchmessage (msg) to process the message", That is, the current handler as the target property of MSG. The Enqueuemessage method of the queue will eventually be called, which means that the message sent by handler will eventually be saved to the message queue.


It is now clear that Looper will call the prepare () and loop () methods to save an Looper instance in the currently executing thread, which will hold a MessageQueue object, and then the current thread into an infinite loop, Messages from the MessageQueue are constantly read from the handler. And then callback the Dispathmessage method in the handler that created the message, let's take a look at this method:

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

As you can see, line 10th, call the Handlemessage method, let's look at this method:

  /**     * Subclasses must implement this to receive messages.     *    /public void Handlemessage (Message msg) {    }    
Can see this is an empty method, for what, because the final callback of the message is controlled by us, we create the handler is the replication Handlemessage method, and then the message processing according to Msg.what.

For example:

Private Handler Mhandler = new Handler () {public void Handlemessage (android.os.Message msg) {switch (msg.what) {case value: Break;default:break;};};

Here, this process has been explained, let's first summarize

1. First Looper.prepare () saves an looper instance in this thread, and then holds a MessageQueue object in the instance, because Looper.prepare () can only be called once in a thread. So MessageQueue will only exist in one thread.

2. Looper.loop () causes the current thread to enter an infinite loop, which reads the message from the MessageQueue instance and then callbacks the Msg.target.dispatchMessage (msg) method.

3, handler the construction method, will first get the current thread saved Looper instance, and Looper instance in the MessageQueue want to associate.

4, Handler SendMessage method, will give the target of MSG to handler itself, and then add MessageQueue.

5. When constructing handler instances, we override the Handlemessage method, which is the method that Msg.target.dispatchMessage (msg) eventually calls.

Well, summing it up, you might also ask, so in activity, we don't show the call to the Looper.prepare () and Looper.loop () methods, why handler can be created successfully, because in the activity's startup code, The Looper.prepare () and Looper.loop () methods have been called in the current UI thread.

3. Handler Post

I was asked today, what do you say Handler's post method creates a thread that has something to do with the UI thread?

In fact, this issue is one of the reasons for this blog, it is necessary to explain, sometimes for convenience, we will directly write the following code:

Mhandler.post (New Runnable () {@Overridepublic void run () {LOG.E ("TAG", Thread.CurrentThread (). GetName ()); Mtxt.settext ("Yoxi");});

Then the Run method can be written to update the UI code, in fact, the runnable did not create a thread, but sent a message, the following look at the source:

Public Final Boolean post (Runnable R)    {       return  sendmessagedelayed (Getpostmessage (R), 0);    }
  private static Message Getpostmessage (Runnable r) {        Message M = Message.obtain ();        M.callback = R;        return m;    }

As you can see, in getpostmessage, we get a message object and assign the Runable object we created as the callback property to this message.

Note: You can create a message object that can be new or use the Message.obtain () method, but the obtain method is recommended, because the message internally maintains a message pool for reuse of message. Avoid using new to reallocate memory.

Public Final Boolean sendmessagedelayed (Message msg, long Delaymillis)    {        if (Delaymillis < 0) {            Delaymillis = 0;        }        Return Sendmessageattime (MSG, systemclock.uptimemillis () + Delaymillis);    }

public boolean sendmessageattime (Message msg, long 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);    }
Finally, like Handler.sendmessage, called the Sendmessageattime, and then called the Enqueuemessage method, assigning Msg.target to Handler, and finally adding messagqueue.

As you can see, the callback and target of MSG here have values, then which one will be executed?

In fact, the above has been posted code, is 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);        }    }
Line 2nd, if not NULL, executes the callback callback, which is our Runnable object.

Well, about Looper, Handler, Message the three relationships above have been described very clearly.

Finally, here's a diagram:


Hope that the picture can better help everyone's memory ~ ~

4, something

In fact, handler can not only update the UI, you can create a handler in a child thread, and then use this handler instance to send a message in any other thread, and the code that eventually processes the message will run in the thread where you created the handler instance.

New Thread () {private Handler handler;public void Run () {looper.prepare (); Looper.loop (); handler = new Handler () {public void Handlemessage (Android.os.Message msg) {LOG.E ("TAG", Thread.CurrentThread (). GetName ());};};};}. Start ();

Android not only provides us with an asynchronous message processing mechanism that allows us to do a better job of updating the UI, but also provides us with a reference to the asynchronous message processing mechanism code ~ ~ Not only to know the principle, it is best to use this design in other non-Android projects ~ ~




Related Article

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.