Android handler messaging mechanism in layman's

Source: Internet
Author: User
Tags message queue

As an Android developer, handler this class should be more familiar, because almost any app development, will use to handler this class, some students may say, I can use asynctask instead of it, this is really possible, But in fact, Asynctask is also achieved through handler, specific people can go to see the source code on the line, the main function of handler is to implement the child thread and the main thread of communication, for example, in the sub-thread to perform some time-consuming operation, the operation is completed after the main thread and the new UI ( Because Android is not allowed in child threads with the new UI).

Let's start this article with a simple example.

public class Mainactivity extends activity{public static final int msg_download_finish=0x001;      Create an anonymous inner class for Handler private Handler handler=new Handler () {@Override public void Handlemessage (Message msg) { Switch (msg.what) {case MSG_DOWNLOAD_FINISH:LOG.V ("Yzy", "handler is in the same thread ID-" +thread.current          Thread (). GetName ());      Break  }    }      };    @Override protected void OnCreate (Bundle savedinstancestate) {super.oncreate (savedinstancestate);  Setcontentview (R.layout.activity_main);          }//Start a download thread public void Download (view view) {new Thread () {public void run () {try {          LOG.V ("Yzy", "Download sub-thread name is--->" +thread.currentthread (). GetName ());          Run on a sub-thread, simulating a download task Thread.Sleep (2000);        After the download is complete, send the download completion message handler.sendemptymessage (Msg_download_finish);        } catch (Interruptedexception e) {e.printstacktrace ();    }      };  }.start (); }

Operation Result:
08-03 16:31:46.418:v/yzy (30486): The name of the download sub-thread is--->thread-22588
08-03 16:31:48.421:v/yzy (30486): The thread where handler is located name is-->main


The above example is to simulate a download task, when the download is complete, send a message through the handler object, and finally be received and processed by the Handlermessage method, through the results can be found, download method is done in the child thread, The handlermessage is called in the UI thread. Here is the end of a simple handler use case, if you have understood the above code, then you have understood the basic use of handler.
Here's a simple example:

public void Postrun (view view)  {    new Thread () {public      void run () {        try        {          log.v ("Yzy", " The name of the download sub-thread is---> "+thread.currentthread (). GetName ());          Thread.Sleep (+);          Handler.post (New Runnable ()          {                        @Override public            Void Run ()            {              log.v ("Yzy", "handler post run"- > "+thread.currentthread (). GetName ());            }          );        catch (interruptedexception e)        {          e.printstacktrace ();        }             };          }. Start ();     }

Operation Result:
08-03 16:31:52.528:v/yzy (30486): The name of the download sub-thread is--->thread-22589
08-03 16:31:54.531:v/yzy (30486): Handler Post run-->main

In handler, you can send a Runnable object in addition to the message object, and the runnable sent from the running result is also executed in the main thread.
Is it true that the message and runnable sent through handler are executed in the UI thread, which is not necessarily, and now I'm creating a handler2 in the OnCreate method

Handlerthread thread=new handlerthread ("Yzy");    Thread.Start ();        Handler2=new Handler (Thread.getlooper ())    {public      void Handlemessage (Message msg) {        switch (msg.what)        {case          msg_download_finish:            log.v ("Yzy", "Handler's thread name is--" +thread.currentthread (). GetName () );            break;};}    ;

Then replace the handler in the above code with the Handler2, then run the code: a thread named "Yzy" is created in the code and then initialized in Yzy thread Handler2
The results are as follows:
08-03 17:07:10.571:v/yzy (8224): The name of the download sub-thread is--->thread-23005
08-03 17:07:12.574:v/yzy (8224): The thread where handler is located name is-->yzy

We found that the message and runnable sent through Handler2 are executed in the Yzy thread.
If you know the reason for the above code and running results, then you are already familiar with the use of handler, if you do not know the result of the above operation, please go ahead.

Before I start analyzing the reasons, I would like to ask the following questions:
1. How does the message and runnable emitted by handler be passed to the UI thread?
2. Under what circumstances does the message and runnable sent through handler not be passed to the UI thread?
3. Why am I initializing a handler in Handlerthread?

Let's solve the above problem one after the other, before we solve the problem we need to analyze a hanlder this kind of source code.


First look at the handler constructor, which has several constructors, let's look at each one.

No parameter constructor public Handler () {//check if Handler is static, if not, then it is possible to cause a memory leak, specifically Baidu if (Find_potential_leaks) {f Inal class<?            Extends handler> klass = GetClass (); if (Klass.isanonymousclass () | | klass.ismemberclass () | | klass.islocalclass ()) && (Klass.getmo Difiers () & modifier.static) = = 0) {LOG.W (TAG, "the following Handler class should be STATIC or leaks            Might occur: "+ klass.getcanonicalname ());        }}//is a very important property, for the time being Looper understood as a message Queue manager, used to fetch messages from the message queue, and later analyze the class Mlooper = Looper.mylooper (); if (Mlooper = = null) {//This exception is very common Oh, handler must be executed on a thread with Looper, which is why I initialize handler//in Handlerthread without initializing it inside the thread, If initialization in thread is required first call the Looper.prepare method throw new RuntimeException ("Can ' t create handler inside THR        EAD that have not called looper.prepare () "); }//Copy the message queue inside the Mlooper to its own mqueue, which means that handler and Looper are public a message queue Mqueue = mlooper.mqueue;//callback functionThe default is null mcallback = NULL; }//this is the same as above, just passed in a callback function public Handler (Callback Callback) {if (Find_potential_leaks) {final class& lt;?            Extends handler> klass = GetClass (); if (Klass.isanonymousclass () | | klass.ismemberclass () | | klass.islocalclass ()) && (Klass.getmo Difiers () & 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 has        Not called Looper.prepare () ");        } mqueue = Mlooper.mqueue;    Mcallback = callback;        }/** * passed in a Looper, and a message queue is shared with Looper */public Handler (Looper Looper) {mlooper = Looper;        Mqueue = Looper.mqueue;    Mcallback = null; }/** * and above aA similar, do not repeat * * public Handler (Looper Looper, Callback Callback) {mlooper = Looper;        Mqueue = Looper.mqueue;    Mcallback = callback; }

Looking at Handler's constructor, I found that there was a message queue in Hanlder, which was passed in from Looper, and handler needed to be called in a thread with Looper. Since it is so closely related to Looper, let's see what Looper is, open the source of Looper, and see an example like this at the top:

*  class Looperthread extends Thread {  * public      Handler Mhandler;  *        * Public      void Run () {  *          looper.prepare ();  *            *          mhandler = new Handler () {  * public              void Handlemessage (Message msg) {  *                  //process Incoming messages here  *              }          ;  *            *          looper.loop ();  *      }  

This is to show that handler can only be created in a thread with Looper, the UI thread is Looper, and for a thread that does not have Looper, you need to call Loope.prepare,looper.loop and other functions, such as the example above.
Then we formally enter the source of Looper to see it

The Looper constructor, which is primarily for Message Queuing properties such as initialization of private Looper () {mqueue = new MessageQueue ();    Mrun = true;//recording the thread mthread = Thread.CurrentThread (); }//In the example above, it is necessary to execute this function if the handler is created in a thread that does not have looper,//This function is primarily a new looper, which is saved in threadlocal, so that a thread is created once. The second call to this method throws an exception to the public static final void prepare () {if (sthreadlocal.get () = null) {throw new Runt        Imeexception ("Only one Looper could be created per thread");    } sthreadlocal.set (New Looper ());    }//prepare is created and saved, this method is to remove Looper public static final Looper Mylooper () {return (Looper) sthreadlocal.get ();    }//This method is called to the system, and the UI thread calls this thread to ensure that there is a looper//in the UI thread: If a thread is a UI thread, then Mylooper and Getmainlooper are the same looper, which is well understood by this code.        public static final void Preparemainlooper () {prepare ();        Setmainlooper (Mylooper ());        if (process.supportsprocesses ()) {Mylooper (). mqueue.mquitallowed = false; }}//Get the UI thread's looper, usually I think Hanlder's handlemessage is usually n when the UI thread executesEW Handler (Getmainlooper ());    Public synchronized static final Looper Getmainlooper () {return mmainlooper;        }/** * The above example is done with this method, which is a dead loop that reads the message from the message queue and distributes it */public static Final void loop () {//Get the looper of this thread        Looper me = Mylooper ();//Get message queue MessageQueue queue = Me.mqueue; while (true) {//takes a message from message queue msg = Queue.next ();//might block//takes a message if (msg! = NULL) {// This is generally not equal to NULL, usually the handler if (Msg.target = = null) that emits this message {//no target is a magic ID                    Entifier for the Quit message.                Return } if (me.mlogging!= null) me.mLogging.println (">>>>> dispatching to "+ Msg.target +" "+ Msg.callback +": "+ msg.what);//Call Handler Dispatch                Message Method Msg.target.dispatchMessage (msg); if (me.mlogging!= null) me.mLogging.println ("<<<<< finished to" + Msg.target + "" + Msg.callback            );//Put the message into the message pool msg.recycle ();    }}}//returns Looper Message Queuing public static final MessageQueue Myqueue () {return Mylooper (). Mqueue; }

After reading the source code of Looper summed up:
In the Run method of a thread first call the Prepare method, the main guarantee that the thread has a Looper object, if you create a hanlder in this thread, then look at the above handler empty construction method, The looper inside the handler is the looper that is stored in the thread, so the handler and threads that can be created are shared by a looper, which is a common message queue. More specifically, a message queue is common to the thread where Hanlder and Looper are located. Finally, the loop method is called, and the thread continuously takes the message from the message queue and then calls the Hanlder DispatchMessage method.

Finally, take a look at some of the other more important ways of handler.

Send a message to message queue in handler, a lot of similar methods, I only choose this one public boolean sendmessageattime (Message msg, long Uptimemillis) {bool        EAN sent = false;        MessageQueue queue = Mqueue;            if (queue! = NULL) {//sets target to handler msg.target = this;        Sent = Queue.enqueuemessage (msg, uptimemillis); } else {RuntimeException e = new RuntimeException (this + "Sendmessageattime () called W            ITH no mqueue ");        LOG.W ("Looper", E.getmessage (), E);    } return sent; }//This method is called in the loop method of public void DispatchMessage (Message msg) {//First check MSG in callback (is a runnable object) is null, if not NULL,        The Run method in callback is called directly if (msg.callback! = null) {handlecallback (msg); } else {//check if callback in handler is empty, if not NULL, call Handlemessage in callback directly and return true to handler I not in call Handlemessage                F (mcallback! = null) {if (Mcallback.handlemessage (msg)) {return; }            }//Last Call to Handlermessage Handlemessage (msg) in handler; }    }

In the previous example, we used the Post method in handler to see how it was implemented.

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);    }

I think if the previous understanding, this will not need me to say.
In fact, in the activity there is a method Runonuithread, is to use this principle to achieve, as follows:

To have Runnable execute public    final void Runonuithread (Runnable action) on the UI thread {//If the current thread is not the UI thread, then forward through handler to the UI thread        if ( Thread.CurrentThread ()! = Muithread) {            mhandler.post (action);        } else {            action.run ();}    }

There are similar methods in view:

public Boolean post (Runnable action) {        Handler Handler;        if (mattachinfo! = null) {            handler = Mattachinfo.mhandler;        } else {            //Assume that post would succeed later< C13/>viewroot.getrunqueue (). Post (action);            return true;        }        Return Handler.post (action);    }

The principle is that the above is the same.
According to the above study, we can draw a conclusion:
1. If handler is created on the UI thread, then handler and the UI thread share Message Queuing, so messages sent through Hanlder are executed in the UI thread.
2. If you want handler messages to be executed in a child thread, it is simple to pass in the looper of the child thread (created by Looper.prepare) when the handler is created
Here believe that I mentioned in the previous three questions have already had the answer, if there is no clear, welcome message ....

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.