Relationship Analysis of handler, loiter, and handlerthread in Android

Source: Internet
Author: User

Relationship Analysis of handler, loiter, and handlerthread in Android

Handlerthread has been used in recent projects and has never been very familiar with its usage. It took some time to study handlerthread during the May Day holiday and back it up on the blog by the way.

Since handlerthread was first studied, we started with handlerthread. Let's take a look at its source code.

/*** Copyright (c) 2006 the android open source project */package android. OS;/*** handy class for starting a new thread that has a logoff. the logoff can then be * used to create handler classes. note that start () must still be called. */public class handlerthread extends thread {int mpriority; // thread priority int mtid =-1; // TID logoff mlogoff; // logoff member variable public handlerthread (string name) {super (name ); Mpriority = process. thread_priority_default; // constructor, set the default thread priority}/*** constructs a handlerthread. */Public handlerthread (string name, int priority) {super (name); mpriority = priority; // constructor, set thread priority based on parameters}/*** call back method that can be explicitly over ridden if needed to execute some * setup before low.loops. */protected void onlooperprepared () {// mainly serves as a subclass for rewriting} public void run () {// run Mtid = process. mytid (); // obtain TID logoff. prepare (); // You Can See synchronized (this) in the logoff class later. {// synchronous mloe = Looper. myloy(); // you can view yyall () in the logoff class; // notify all threads} process. setthreadpriority (mpriority); // set the thread priority onlooperprepared (); // this parameter is not implemented in the handlerthread class. sub-classes can be implemented and some pre-processing operations are performed in logoff. loop (); // You Can See mtid =-1;}/*** this method returns the logoff associated with this thread in the logoff class. if this thread not been s Tarted * or for any reason is isalive () returns false, this method will return null. if this thread * has been started, this method will block until the loginhas been initialized. * @ return The logoff. */Public Looper getlooper () {If (! Isalive () {// If the thread is dead, return NULL;} // If the thread has been started, wait until the looper has been created. synchronized (this) {While (isalive () & mlove = NULL) {// If the thread is alive but is null, it waits, consistent with synchronized in the run method, try {Wait ();} catch (interruptedexception e) {}} return mlotion; // return The logoff member variable}/*** ask the currently running loning to quit. if the thread has not * been started or Has finished (that is if {@ link # getlooper} returns * null), then false is returned. otherwise the logoff is asked to * Quit and true is returned. */Public Boolean quit () {Looper = getlooper (); If (Looper! = NULL) {logoff. quit (); // return true;} return false;}/*** returns the identifier of this thread. see process. mytid (). */Public int getthreadid () {// return TID return mtid ;}}

There are not many source codes. Some of them involve methods in the logoff class. Let's take a look at the logoff class source code (only analyze the involved methods and member variables ).

/*** Copyright (c) 2006 the android open source project */package android. OS; import android. util. log; import android. util. printer; import android. util. prefixprinter; public class logoff {Private Static final string tag = "logoff"; // sthreadlocal. get () will return null unless you 've called prepare (). static final threadlocal <Looper> sthreadlocal = new threadlocal <lolocal> (); Final messagequeue mqueue; Final thread mthread; volatile Boolean mrun; private printer mlogging = NULL; Private Static logoff mmainlogoff = NULL; // guarded by logoff. class/** initialize the current thread as a looper. * This gives you a chance to create handlers that then reference * This logoff, before actually starting the loop. be sure to call * {@ link # loop ()} after calling this method, and end it by calling * {@ Lin K # Quit ()}. */public static void prepare () {If (sthreadlocal. Get ()! = NULL) {Throw new runtimeexception ("only one logoff may be created per Thread");} sthreadlocal. set (New looer (); // put the looer variable related to the current thread (refer to the looer constructor) into sthreadlocal}/*** run the message queue in this thread. be sure to call * {@ link # Quit ()} to end the loop. */public static void loop () {Looper me = mylooper (); // obtain the looper variable related to the current thread if (Me = NULL) {Throw new runtimeexception ("No logoff; logoff. P Repare () wasn' t called on this thread. ");} messagequeue queue = me. mqueue; // messagequeue Of The logoff variable related to the current thread // make sure the identity of this thread is that of the local process, // and keep track of what that identity Token actually is. binder. clearcallingidentity (); Final long ident = binder. clearcallingidentity (); While (true) {// infinite loop yo message MSG = queue. next (); // might block, get message if (msg! = NULL) {If (msg.tar get = NULL) {// No target is a magic identifier for the quit message. return;} Long wallstart = 0; long threadstart = 0; // This must be in a local variable, in case a UI event sets the logger printer logging = me. mlogging; If (logging! = NULL) {logging. println (">>>>> dispatching to" + msg.tar get + "" + MSG. callback + ":" + MSG. what); wallstart = systemclock. currenttimemicro (); threadstart = systemclock. currentthreadtimemicro ();} msg.tar get. dispatchmessage (MSG); // What is the target of the message? That's right. It's handler. Let's take a look at the handler class if (logging! = NULL) {long walltime = systemclock. currenttimemicro ()-wallstart; long threadtime = systemclock. currentthreadtimemicro ()-threadstart; logging. println ("<finished to" + msg.tar get + "" + MSG. callback); If (logging instanceof Profiler) {(Profiler) logging ). profile (MSG, wallstart, walltime, threadstart, threadtime) ;}// make sure that during the course of dispatching the // identity o F The thread wasn't upted. Final long newident = binder. clearcallingidentity (); If (ident! = Newident) {log. WTF (TAG, "thread identity changed from 0x" + long. tohexstring (ident) + "to 0x" + long. tohexstring (newident) + "while dispatching to" + msg.tar get. getclass (). getname () + "" + MSG. callback + "What =" + MSG. what);} MSG. recycle (); // recycle and use the message, so we will use the obtain method more when using the message later. }}}/*** Return the loginobject associated with the current thread. returns * null if the calling thread is not associated with a logoff. */public static logoff mylogoff () {return sthreadlocal. get ();}/*** return the {@ link messagequeue} object associated with the current * thread. this must be called from a thread running a logoff, or a * nullpointerexception will be thrown. */public static messagequeue myqueue () {return myloue (). mqueue;} private Looper () {mqueue = new messagequeue (); mrun = true; mthread = thread. currentthread ();} public void quit () {message MSG = message. obtain (); // Note: By enqueueing directly into the message queue, the // message is left with a null target. this is how we know it is // a quit message. mqueue. enqueuemessage (MSG, 0 );}}

Let's take a look at the source code of the handler class.

/*** Copyright (c) 2006 the android open source project */package android. OS; import android. util. log; import android. util. printer; import Java. lang. reflect. modifier;/*** a handler allows you to send and process {@ link message} and runnable * objects associated with a thread's {@ link messagequeue }. each handler * instance is associated with a single thread and that thread's message * queue. when you Create a new handler, it is bound to the thread * message queue of the thread that is creating it -- from that point on, * It will deliver messages and runnables to that message queue and execute * them as they come out of the message queue. */public class handler {Private Static final Boolean find_potential_leaks = false; Private Static final string tag = "handler";/*** callback interface you Can use when instantiating a handler to avoid * having to implement your own subclass of handler. */public interface callback {public Boolean handlemessage (Message MSG);}/*** subclasses must implement this to receive messages. */Public void handlemessage (Message MSG) {}/ *** handle system messages here. */Public void dispatchmessage (Message MSG) {// important, in Lopper. in the loop () method, this method is called if (msg. Callback! = NULL) {handlecallback (MSG); // The callback member variable of MSG is of the runnable type. MSG is executed here. callback. run (), but we generally do not assign a value to the callback member variable of the message.} Else {If (mcallback! = NULL) {// if we assign a value to the member variable of the callback type of the new () handler class, it will be executed below, but we generally do not. If (mcallback. handlemessage (MSG) {return ;}} handlemessage (MSG); // We generally perform the operation we want here. This method needs to inherit the handler class and then override it. }/*** Default constructor associates this handler with the queue for the * Current thread. ** if there isn't one, this handler won't be able to receive messages. */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 shocould be static or leaks might occur:" + Klass. getcanonicalname () ;}} mlooper = Looper. mylooper (); If (mloexception = NULL) {Throw new runtimeexception ("can't create handler inside thread t Hat has not called logoff. prepare () ");} mqueue = mloue. mqueue; mcallback = NULL;}/*** constructor associates this handler with the queue for the * Current thread and takes a callback interface in which you can handle * messages. */Public handler (callback) {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 shocould be static or leaks might occur:" + Klass. getcanonicalname () ;}} mlooper = Looper. mylooper (); If (mloexception = NULL) {Throw new runtimeexception ("can't create handler inside thread that has not called loled. prepare () ");} mqueue = mloue. mqueue; mcallback = callback;} public final message obtainmessage () {return message. obtain (this); // obtain MSG. The obtain () method of MSG is called at last, and the target of MSG is this, that is, the caller handler}/*** use the provided queue instead of the default one. */Public handler (low.logoff) {mlogoff = logoff; mqueue = logoff. mqueue; mcallback = NULL;}/*** use the provided queue instead of the default one and take a callback * interface in which to handle messages. */Public handler (Looper, callback) {mloue = loue; mqueue = loue. mqueue; mcallback = callback;} // if we can get rid of this method, the handler need not remember its loop // We cocould instead export a getmessagequeue () method... public final Looper getlooper () {return mlooper;} private final void handlecallback (message) {message. callback. run ();} final messagequeue mqueue; Final Looper mloue; final callback mcallback; imessenger mmessenger ;}

The source code of the loose and handler classes is incomplete, but the member variables and methods related to handlerthread are analyzed.

After our step-by-step analysis, the idea is clear:

1. When handlerthread runs to logoff. Loop (), it keeps endless loops. Msg.tar get. the dispatchmessage (MSG) method allows the MSG target (when the MSG is obtained through the handler's obtainmessage () series method, the MSG target is the handler) that is, Handler uses its handlemessage (MSG) method to process MSG.

2. But where does MSG come from? By looking at the source code, we can find that it is obtained through the mqueue. Next () method of the messagequeue type variable of the current thread's logoff.

3. Handler also has a messagequeue type variable. So, how does the messagequeue variable of handler have a relationship with the messagequeue type variable of the logoff class?

4. Do you remember that the handler class has a constructor called public handler (logoff )? By the way, this constructor creates a relationship between them. (Handlerthread has a loose type variable that can be obtained through the getlooper () method and passed in to the handler class constructor. In this way, handler is associated with handlerthread ).

5. So that the last handlerthread operation is the same messagequeue as the handler.

6. handler can put MSG into messagequeue through sendmessage (MSG) series methods, then handlerthread obtains MSG from messagequeue, and then calls handlemessage (MSG) method of handler for processing.

A general summary of the running process: Handler puts the message into messagequeue, handlerthread keeps getting the message from messagequeue, and then calls handler's handlemessage (Message) method to continuously process the message.

After analysis, we will write a demo to test the thread with Message Queue (non-main thread ):

The project is as follows:

Handlerthread analysis demo

The following are my test results:

The result shows that handlerthread does maintain a message queue. From the log, we can see that their TID is the same.

Summary:

1. handlerthread maintains a message queue so that we do not need to create new objects everywhere. However, it has been running in the background (endless loop). In this regard, it is still resource-consuming.

2. There is actually a buffer + recycling mechanism for messages. In the future, messages will be obtained using the obtain () series of methods, which can save resources and improve efficiency.

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.