Message: messages, which contain message IDs, message processing objects, and processed data, are unified by MessageQueue, and eventually handled by handler.
Handler: Processor, responsible for message delivery and processing. When using handler, you need to implement the Handlemessage (Message msg) method to handle a particular message, such as updating the UI.
MessageQueue: Message Queuing, which is used to store messages sent by handler and executed in accordance with FIFO rules. Of course, storing the message is not the actual meaning of the save, but the message is linked to the list of ways, waiting for looper extraction.
looper: Message pump, constantly extract messages from the MessageQueue execution. Therefore, a MessageQueue needs a looper.
Thread: threads, which are responsible for scheduling the entire message loop, which is the execution site of the message loop.
The Android message queue and message loop are all for specific threads, one thread can exist (or not exist) a message queue and a message loop (Looper), messages for a particular thread can only be distributed to this thread, not cross threads, and communicate across processes. However, the worker threads created by default do not have message loops and message queues, and if you want the thread to have Message Queuing and message loops, you need to call Looper.prepare () first in a thread to create the message queue, and then call Looper.loop () into the message loop. As shown in the following example:
Looperthread Thread {
Handler mhandler;
Run () {
looper.prepare ();
Mhandler = Handler () {
handlemessage (message msg) {
}
};
Looper.loop ();
}
//looper class Analysis
//did not find the right way to parse the code. Each important line is preceded by a comment
the code for //functionality adds an analysis before the code
public class Looper {//static variable to determine whether debug information is printed.
Private static Final Boolean DEBUG = false; Private static Final Boolean LOCALLOGV = DEBUG?
Config.LOGD:Config.LOGV;
Sthreadlocal.get () would return null unless for you ' ve called prepare (). The encapsulation of the thread-local storage functionality, Tls,thread, storage, what does it mean? Because storage is either on the stack, such as internal variables defined within the function.
Either on the heap, such as new or malloc, but now systems such as Linux and Windows provide a thread-local storage space, which is a thread-dependent storage space within a thread, so that the thread-related stuff is stored in the
In TLS of this thread, you do not have to put it on the heap to synchronize.
private static final ThreadLocal sthreadlocal = new ThreadLocal ();
Message queue, MessageQueue, look at the name and know it's a queue.
Final MessageQueue Mqueue;
Volatile Boolean mrun;
The thread associated with this looper is initialized to the null thread mthread;
Private Printer mlogging = null;
A static variable that represents a UI process (or a service bar, where the default is the UI) is the main thread private static looper mmainlooper = null;
/** Initialize The current thread as a looper. * This gives your a chance to create handlers which then reference * This looper, before actually the loop. Being sure to call
* {@link #loop ()} After calling this method, and end it by calling * {@link #quit ()}. * *///To set up this Looper object in TLS, if this thread has already set looper, it will make an error//This means that a thread can only set one looper public static final void prepare () {if (s)
Threadlocal.get ()!= null) {throw new RuntimeException ("Only one looper may is created per thread");
} sthreadlocal.set (New Looper ()); /** Initialize The current thread as a looper, marking it as a application ' main * looper. The main looper for your application are created by the Android environment and * so you should never need to call this fun
Ction yourself. * {@link #prepare ()}///The main message loop of the UI program set by the framework, note that this main message loop is not actively exited//public static final void Preparemainloop
ER () {prepare ();
Setmainlooper (Mylooper ()); Determine if the main message loop can exit ...//exit request Looper by quit function if (process.supportsprocesses ()) {Mylooper (). mqueue.mquitallowed
= false; } private synchronized static void Setmainlooper (LooPer looper) {mmainlooper = Looper;
}/** Returns The application ' s main Looper, which lives in the main thread of the application.
* * Public synchronized static final Looper Getmainlooper () {return mmainlooper; /** * Run the message \ Queue in this thread.
Be sure to call * {@link #quit ()} to end the loop.
*//message loop, the entire program is here while.
This is the static function. public static final void loop () {Looper me = Mylooper ();//Remove the corresponding Looper object from the thread MessageQueue queue = me.mqueue;//Fetch
Message Queuing Object ... while (true) {msg = Queue.next ();//might a pending message in the message queue ... if (!me.mrun) {//Do you need to exit?
Mrun is a volatile variable that is synchronized across threads and should have a place to set it.
Break } if (msg!= null) {if (Msg.target = = null) {//No target is a magic identifier for the qui
T message.
Return } if (me.mlogging!= null) me.mLogging.println (">>>>> dispatching to" + Msg.target +
" " + Msg.callback + ":" + msg.what);
Msg.target.dispatchMessage (msg);
if (me.mlogging!= null) me.mLogging.println ("<<<<< finished to" + Msg.target + ""
+ Msg.callback);
Msg.recycle (); }}/** * Return the Looper object associated with the current thread.
Returns * NULL if the calling thread is isn't associated with a looper.
*//return the thread-related looper public static final Looper Mylooper () {returns (Looper) Sthreadlocal.get (); /** * Control logging of messages as they are from this processed. IF * enabled, a log message would be written to <var>printer</var> * at the beginning and ending of each m
Essage Dispatch, identifying the * target Handler and message contents.
* * @param printer A Printer object that would receive log messages, or * Null to disable message logging.
*//Set debug Output object, Looper cycle will print the relevant information, used for debugging the best. public void SetmEssagelogging (Printer Printer) {mlogging = Printer; /** * Return the {@link MessageQueue} object associated with the current * thread.
This must is called from a thread running a looper, or a * nullpointerexception would be thrown.
* * public static final MessageQueue Myqueue () {return Mylooper (). Mqueue;
//Create a new Looper object,//internally allocate a message queue, set Mrun to True private Looper () {mqueue = new MessageQueue ();
Mrun = true;
Mthread = Thread.CurrentThread ();
public void Quit () {msg = Message.obtain (); Note:by enqueueing directly into the "message queue,"//message being left with a null target.
This are how we know it's//a quit message.
Mqueue.enqueuemessage (msg, 0);
}/** * Return the Thread associated with this looper.
* * Public Thread GetThread () {return mthread;
//Back is simple, print, exception definition, etc.
public void Dump (Printer pw, String prefix) {pw.println (prefix + this);
Pw.println (prefix + "mrun=" + mrun); Pw.println (prefix+ "mthread=" + mthread);
Pw.println (prefix + "mqueue=" + ((mqueue!= null)? Mqueue: "(null)");
if (mqueue!= null) {synchronized (mqueue) {message msg = mqueue.mmessages;
int n = 0;
while (msg!= null) {pw.println (prefix + "message" + N + ":" + msg);
n++;
msg = Msg.next;
} pw.println (prefix + "(Total messages:" + n +) ");
}} public String toString () {return ' looper{' + integer.tohexstring (System.identityhashcode (This))
+ "}"; The static class Handlerexception extends Exception {handlerexception (message message, Throwable cause) {Super (
CreateMessage (cause), cause);
static string CreateMessage (Throwable cause) {string causemsg = Cause.getmessage ();
if (causemsg = = null) {causemsg = Cause.tostring ();
return causemsg;
}
}
}
How do you send messages to this message queue?? Call the Looper static function myqueue Get Message Queuing so you can insert messages into the inside yourself. But this method is more troublesome, this time handler class to play a role. Take a look at handler's code first, you'll see.
Class handler{...//handler default constructor public Handler () {//What is the use of this is not yet understood, related to the deep content of Java should be if (find_poten
Tial_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:"
+ Klass.getcanonicalname ());
}//Get the Looper object for this thread///If this thread has not yet set looper, this throws an exception Mlooper = Looper.mylooper (); if (Mlooper = = null) {throw new RuntimeException ("Can" t create handler inside thread that is has not called
Looper.prepare () ");
//shameless Ah, directly to the Looper queue and their own queue to make a//So, I through the handler packaging mechanism to add the message, it is equivalent to add directly to the Looper message queue to Mqueue = Mlooper.mqueue;
Mcallback = null; //There are several constructors, one with callback, one with Looper//externally set Looper public Handler (Looper looper) {mlooper = Looper;
Mqueue = Looper.mqueue;
Mcallback = null; ///With callback, a handler can set a callback. If there is a callback,//usually sent to the message through this Handler, there are callback processing, equivalent to a total concentration of processing//later see DispatchMessage when the analysis of public Handler (Looper
Looper, Callback Callback) {mlooper = Looper;
Mqueue = Looper.mqueue;
Mcallback = callback; The//////////////The internal sendmessagedelayed public final Boolean SendMessage is called via handler (message msg) {return SENDM
essagedelayed (msg, 0); //FT, another layer is encapsulated, which is called sendmessageattime//because the delay time is based on the current call time, so it is necessary to obtain absolute time pass to the Sendmessageattime public final Boolean
sendmessagedelayed (Message msg, long Delaymillis) {if (Delaymillis < 0) {Delaymillis = 0;
Return Sendmessageattime (MSG, systemclock.uptimemillis () + Delaymillis);
public boolean sendmessageattime (msg, long Uptimemillis) {Boolean sent = false;
MessageQueue queue = Mqueue;
if (queue!= null) {//Set target of the message to itself, then add to message Queue//For the data structure of the queue, the operation is simpler. Msg.target = this;
Sent = Queue.enqueuemessage (msg, uptimemillis); else {runtimeexception e = new RuntimeException (this + sendmessageattime () called with no Mqueu
E ");
LOG.W ("Looper", E.getmessage (), E);
return sent; //Remember that message loop in Looper?//When a message is received from a message queue, the target of its target dispatchmesage function//message is already set to handler, so/
It will eventually be transferred to the MSG processing of handler//There's a problem with the process. public void DispatchMessage (msg) {//If MSG itself is set to callback, the callback is processed directly
if (msg.callback!= null) {handlecallback (msg); else {//If the callback of the handler is available, the callback is processed---equivalent to centralizing the IF (mcallback!= null) {if (mcallback.handle
Message (msg)) {return;
}//Otherwise to the derivation processing, the default processing of the base class is nothing dry handlemessage (msg);
}
}
..........
}
Generated
Message msg = Mhandler.obtainmessage ();
Msg.what = what;
Msg.sendtotarget ();
Send
MessageQueue queue = Mqueue;
if (queue!= null) {
msg.target = this;
Sent = Queue.enqueuemessage (msg, uptimemillis);
}
In Handler.java's sendmessageattime (Message msg, long Uptimemillis) method, we see that it finds the MessageQueue it references, The target of the message is then set to itself (for the purpose of processing the messages in order to find the correct handler), and then the message is included in the queue.
Extraction
Looper me = Mylooper ();
MessageQueue queue = Me.mqueue;
while (true) {
msg = Queue.next ();//might block
if (msg!= null) {
if (Msg.target = = null) {
// No Target is a magic identifier for the quit message.
return;
}
Msg.target.dispatchMessage (msg);
Msg.recycle ();
}
In the loop () function of the Looper.java, we see that there is a dead loop, constantly fetching the next (next method) message from the MessageQueue, and then using the target information carried in the messages, Referred to the correct handler treatment (DispatchMessage method).
Processing
if (msg.callback!= null) {
handlecallback (msg);
} else {
if (mcallback!= null) {
if (mcallback.hand Lemessage (msg)) {return
;
}
}
Handlemessage (msg);
In Handler.java's DispatchMessage (Message msg) method, one of the branches is to invoke the Handlemessage method to process this message, And that's why we need to implement Handlemessage (message msg) When we describe the use of handler in our duty department.
As for the other branch of the DispatchMessage method, I will explain it later in this section.
At this point, we see, a message through the handler sent, MessageQueue team, looper extraction, and once again back to handler embrace. The circle around it also helps us to turn the synchronization operation into an asynchronous operation.
3 The rest of the section, we'll discuss the threading of handler and how to update the UI.
In the main thread (UI thread), if the Looper object is not passed in when the handler is created, then the Looper object of the main thread (UI thread) is used directly (the system has been created for us), and in other threads, if the Looper object is not passed in when the handler is created, This handler will not receive processing messages. In this case, the common practice is to:
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 ();
}
Before creating the handler, prepare a looper (looper.prepare) for the thread, and then let the Looper run (looper.loop) and extract the message so that handler can work correctly.
Therefore, handler processing messages are always run in threads that create handler. In our message processing, there is no shortage of updates to the UI, and an incorrect thread to update the UI directly throws an exception. Therefore, you need to always care about which line the handler Chengri created.
How can I update the UI to make it no exception? The SDK tells us that there are 4 ways to access the UI thread from other threads:
· Activity.runonuithread (Runnable)
· View.post (Runnable)
· View.postdelayed (Runnable, long)
· Handler
Among them, the emphasis is on the View.post (Runnable) method. In the post (Runnable action) method, view obtains the handler of the current thread (that is, the UI thread), and then posts the action object into handler. In handler, it wraps the action object passed over into a message (the callback of messages is action) and puts it into the UI thread's messaging loop. When handler processes the message again, a branch (the one that is not explained) is set up for it, calling the runnable run method directly. At this point, it has been routed to the UI thread, so we can update the UI without any hesitation.
4) Some summary
· The handler process runs during the creation of the handler line Chengri
· A looper corresponds to a MessageQueue
· A thread corresponds to a looper
· A looper can correspond to multiple handler
· When the current thread is not determined, try to invoke the Post method when updating the UI