Principle analysis of thread, Handler, Looper and MessageQueue in Android

Source: Internet
Author: User

In Android development, the classes Thread, Handler, looper are especially common and may not be very clear when you start learning Android. Let's take a look at how these several classes work together from the source point of view.

Thread

The first is thread, and we all know that a thread is a threaded object that can be multithreaded if you fill out your code in the Run method and start the thread. For example:

        New Thread () {public            void run () {                //time-consuming Operation            };        }. Start ();
we know that for the above code, when you finish executing the operation in run, the entire thread ends and does not continue. Our application will be executed until you exit or the application throws an exception. This introduces another concept, Message Queuing. When the Android app starts, it defaults to a main thread (the UI thread), where a message queue is associated, and all operations are encapsulated into a message and then handed to the main thread for processing. In order to ensure that the main thread does not actively exit, the action of canceling the interest is placed in a dead loop, which is equivalent to the execution of a dead loop and therefore does not exit.

The example diagram is as follows:


The entry for the Android application is the Activitythread.main method, please refer to the source code analysis of the Android application process startup process, the UI thread's message loop is created in this method, the source code is as follows:

 public        static void Main (string[] args) {Samplingprofilerintegration.start ();        Closeguard.setenabled (FALSE);        Environment.initforcurrentuser ();        Set The reporter for event logging in Libcore eventlogger.setreporter (New Eventloggingreporter ());        Process.setargv0 ("<pre-initialized>");        Looper.preparemainlooper ();//1, create message loop Looper activitythread thread = new Activitythread ();        Thread.attach (FALSE); if (Smainthreadhandler = = null) {Smainthreadhandler = Thread.gethandler ();//UI thread Handler} Asy        Nctask.init (); if (false) {Looper.mylooper (). setmessagelogging (New Logprinter (Log.debug, "Activitythread")        );   } looper.loop ();    2. Execute message loop throw new RuntimeException ("Main thread loop unexpectedly exited"); }
after the Activitythread.main method is executed, the application starts, and the message is taken from the message queue, and then the message is processed. So how does the system post messages to the message queue? And how do you get the message from the message queue and process the message? The answer is handler.

Handlerin many cases we need to update the UI after we have done the time-consuming operations on the child threads, but we all know that we cannot update the UI in a child thread. The most common way to do this is to post a message to the UI thread through handler and then process it in the handler Handlemessage method. But one point to note is that the handler must be created in the main thread!! A Simple example is as follows:

    Class MyHandler extends Handler {        @Override public        void Handlemessage (Message msg) {          //update UI                  }    }     MyHandler Mhandler = new MyHandler ();    Opens a new thread    () {public            void run () {                ///time-consuming Operation                Mhandler.sendemptymessage (123);}            ;        }. Start ();

Why do we have to do this? In fact, each handler is associated with a message queue, Message Queuing is encapsulated in Lopper, and each looper is associated with a thread (ThreadLocal), that is, each message queue is associated with a thread. Handler is a message handler that posts messages to the message queue, which is then fetched from the message queue by the corresponding thread and executed. By default, Message Queuing has only one, that is, the main thread's message queue, which is created in the Activitythread.main method, created by Lopper.preparemainlooper (), and then finally executed Looper.loop () To start the message loop. So how does handler correlate Message Queuing with threads? Let's look at the following source code:

    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 ();   Get Looper        if (mlooper = = null) {            throw new RuntimeException (                "Can ' t create handler inside thread that has Not called Looper.prepare () ");        }        Mqueue = Mlooper.mqueue;       Get Message queue        mcallback = null;    }
From the handler default constructor, we can see that the handler is internally Looper.getlooper () to get the Looper object, and it is associated with it, and most importantly, Message Queuing. So how does Looper.getlooper () work? Let's keep looking down.

    /** * Return The Looper object associated with the current thread.     Returns * NULL if the calling thread is not associated with a Looper.    */public static Looper Mylooper () {return sthreadlocal.get (); }/** * Initialize the current thread as a looper, marking it as an * application ' s main looper. The main looper for your application * are created by the Android environment, so we should never need * to call T  His function yourself.        See also: {@link #prepare ()} */public static void Preparemainlooper () {prepare ();        Setmainlooper (Mylooper ());    Mylooper (). mqueue.mquitallowed = false;    } private synchronized static void Setmainlooper (Looper Looper) {mmainlooper = Looper;      }/** Initialize the current thread as a looper. * This gives you a chance to create handlers so reference * This looper, 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 Looper ()); }
we see that the Mylooper () method is obtained through Sthreadlocal.get (), for information on threadlocal, please refer to threadlocal multi-threaded instance.  So when did the Looper object be stored in sthreadlocal? Sharp-eyed's friend may have seen that the code posted above gives a familiar method, Preparemainlooper (), in which the prepare () method is called and a Looper object is created in this method, And the object is set to Sthreadlocal. In this way, the queue is associated with the thread!!! Different threads are unable to access each other's message queues. Back in handler, Message Queuing is associated with threads through Looper, and handler is associated with Looper, so handler is eventually associated with thread and thread Message Queuing. This explains the above question, "Why do I need to update the UI for handler to be created in the main thread?" ”。 this is because the handler to be associated with the main thread's message queue so that the handlemessage executes on the UI thread, and the UI update is thread safe!!!

Looper and MessageQueue

After creating the Looper, how do you execute the message loop? How is the message processed by handler to the message queue (linked list)? The answer is that in the message loop, the message loop is built by means of the Looper.loop () method. The source code is as follows:

    /** * Run The message queue in this thread.     Be sure to call * {@link #quit ()} to end of the loop.        */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; 1. Get Message Queue//code omitted while (TRUE) {//2, dead loop, message loop msg = Queue.next (); 3. Get message (might block) if (msg! = null) {if (Msg.target = = null) {//No T                    Arget 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);            4, processing messages//code omitted msg.recycle (); }        }    }
as you can see, the loop method essentially establishes a dead loop, and then processes the message by taking the message out of the message queue and finally processing it. For Looper We summarize: Create Looper objects with Looper.prepare () (Message Queuing encapsulated in Looper objects), save in Sthreadloal, and then execute the message loop through Looper.loop ().   These two steps are usually paired up!!


Finally, we look at the message handling mechanism, and we see that the 4th step in the code is to process the message through Msg.target.dispatchMessage (msg). Where MSG is the message type, we look at the source code:

Public final class Message implements parcelable {public int-what    ;    public int arg1;     public int arg2;    public Object obj;    int flags;    long when;        Bundle data;        Handler Target;         Target processing        Runnable callback;      Runnable type callback        //Sometimes we store linked lists of these things    Message next;           Next message, Message Queuing is a chained store    //code omission ....    }

As you can see from the source code, target is the handler type. In fact, it's a turn around, the message is delivered to the message queue via handler, and Message Queuing sends the message to handler for processing. Let's keep looking .
    /**     * Subclasses must implement this to receive messages.     */Public    void Handlemessage (Message msg) {    }    private final void handlecallback (Message message) {        Message.callback.run ();    }        /**     * Handle system messages here.     */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, DispatchMessage is just a method of distribution, and if the runnable type of callback is empty then Handlermessage is executed to process the message, and the method is empty, and we will write the code of the update UI in the function If callback is not empty, the handlecallback is executed and the method calls the callback Run method. In fact, this is the handler distribution of two types, such as our post (Runnable callback) callback is not empty, when we use handler to SendMessage when we usually do not set callback, Therefore, this branch of Handlermessage is also executed. Let's look at two implementations:

    Public Final Boolean post (Runnable R) {return sendmessagedelayed (Getpostmessage (R), 0);        Private final Message Getpostmessage (Runnable r) {Message M = Message.obtain ();        M.callback = R;    return m;            Public Final Boolean sendmessagedelayed (Message msg, long Delaymillis) {if (Delaymillis < 0) {        Delaymillis = 0;    } return Sendmessageattime (MSG, systemclock.uptimemillis () + Delaymillis);        The public boolean sendmessageattime (Message msg, long Uptimemillis) {Boolean sent = false;        MessageQueue queue = Mqueue;      if (queue! = null) {Msg.target = this;  Sets the target of the message to the current handler object sent = Queue.enqueuemessage (msg, uptimemillis); Insert message into message queue} else {RuntimeException e = new RuntimeException (this + "sendmes            Sageattime () called with no Mqueue ");        LOG.W ("Looper", E.getmessage (), E); } return sent; }

As you can see, at post (Runnable R), the Runnable is wrapped into a message object, and the Runnable object is set to the callback field of the Message object, and the message object is then inserted into the queue of messages. SendMessage is a similar implementation:

    Public Final Boolean sendMessage (Message msg)    {        return sendmessagedelayed (msg, 0);    }
The sendmessagedelayed (msg, time) method is invoked whether it is a post runnbale or a message.

Why does creating handler in a child thread throw an exception?Let's look at the following code:
        New Thread () {            Handler Handler = null;            public void Run () {                handler = new handler ();            };        }. Start ();
is there a problem with the code above? If you can find and explain the problem with the above code, then you should say that you have a good understanding of the concepts of handler, Looper, and thread. If you are not sure, then we will study together. as mentioned earlier, the Looper object is threadlocal, that is, each thread has its own looper, and this looper can be empty. But when you want to create a handler object in a sub-thread, if Looper is empty, it throws "can ' t create handler inside thread that have not called looper.prepare ()" Exception, Why is that? Let's look at the source code together.
   /**     * Default Constructor Associates This handler with the ' queue for the '     * current thread.     *     * If there isn ' t one, this handler won ' t is able to receive messages.     *    /Public Handler () {       //code omitted         mlooper = Looper.mylooper ();    Get Mylooper        if (mlooper = = null) {            throw new RuntimeException (                "Can ' t create handler inside thread that h As not called Looper.prepare () ");//Throw exception        }        mqueue = Mlooper.mqueue;        Mcallback = null;    }

We can see that when the Mlooper object is empty, the exception is thrown. This is because the Looper object in the thread has not yet been created, so Sthreadlocal.get () returns NULL. Here's how to fix it:
        New Thread () {            Handler Handler = null;            public void Run () {                looper.prepare ();    1. Create Looper, and bind to threadlocal                handler = new handler ();                Looper.loop ();       2. Start the message loop            };        }. Start ();
in the code we add 2, the first is to create the Looper through Looper.prepare (), and the second is to start the message loop through Looper.loop (). This way the thread has its own looper, which means it has its own message queue. If you create a looper without starting the message loop, you will not throw an exception, but you will not be able to post or SendMessage through handler, as the message is appended to the message queue, but the message loop is not started. It will not get the message from the message queue and execute it!
SummarizeWhen the app starts, a main thread (UI thread) is turned on, and the message loop is started, and the app keeps extracting from the message queue and processing the message to the effect of running the program. The Looper object encapsulates Message Queuing, Looper objects are threadlocal, and Looper objects between different threads cannot be shared and accessed. While handler binds to the execution thread by binding with the Looper object, handler appends the runnable (wrapped as a message) or the message object to the queue of messages associated with the thread, and then takes the message out of the message loop and processes the message. When the handler bound Looper is the looper of the main thread, the handler can update the UI in Handlemessage, or the update UI throws an exception!In fact, we can think of handler, Looper, thread as a production line, workers (porters) equivalent to handler, responsible for the transport of goods to the belt (handler the message to the message queue); The conveyor plays the role of Message Queuing, responsible for delivering the goods, The goods are removed and transported to the destination (target), and the goods arrive at a workshop and are then processed by the workers, the workshop plays the role of thread, each workshop has its own independent conveyor belt, workshop A's goods can not be obtained by Workshop B, that is equivalent to threadlocal ( Workshop exclusive).

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.