Applications in the Android system, like Java applications, are message-driven, simply put: There is a message queue, we can constantly add messages to this message queue, and remove the message from it, processing the message. The main work related to this job in Android is done by Handler,looper and message.
- Looper class: A message loop is run for a thread, there is a message queue inside, and each thread only allows a maximum of one looper;
- Handler class: Allows you to send messages to a thread's message queue and process messages;
- Message class: Messaging class.
Using the sample
First, we learn how to use, Looper, and handler through a simple example, and study how it works through this example;
1. We prepare for the message loop in Looperthread and create a handler to process the message, noting that handler is created after calling Looper.prepare ();
Public class looperthread extends Thread { PublicHandler Mhandler;@Override Public void Run() {//TODO auto-generated method stubLooper.prepare ();synchronized( This) {Mhandler =NewHandler () {@Override Public void Handlemessage(Message msg) {//TODO auto-generated method stub Super. Handlemessage (msg); LOG.W ("Looperthread","Handlemessage::thread ID---"+ getId ()); } }; } looper.loop (); Notifyall (); }}
2. Next we create a new thread in the main thread and send a message to the Looperthread thread through handler in Looperthread;
FinalLooperthread Mlooperthread =NewLooperthread (); Mlooperthread.start ();NewThread () {@Override Public void Run() {//TODO auto-generated method stub while(Mlooperthread.mhandler = =NULL) {Try{Wait ();//Prevent handler from being created when sending messages}Catch(Interruptedexception e) {//TODO auto-generated catch blockE.printstacktrace (); }} mLooperThread.mHandler.sendEmptyMessage (0); LOG.W (TAG,"Send message::thread ID---"+ getId ()); }}.start ();
This example essentially creates a new thread and sends a message to looperthread in that thread. In order to prevent the handler from being established while sending the message, a synchronization is made, and when Mhandler is null, the thread is blocked to wait for the thread to wake up when the handler is established. You can see the results in log:
Looper analysis
From the above example we can see that if we want to do message management in this thread, we first need to call the Looper.prepare method, so let's first look at this method:
public Static void prepare () {Prepare (true ); } private static void prepare (Boolean quitallowed) {if (ST Hreadlocal. get ()! = null ) {throw new runtimeexception (" only one Looper could be created per thread "); } sthreadlocal. set (new Looper (quitallowed)); }
You can see that in the Prepare method, you first determine whether Looper has been created in the thread, and then call the set method of Threadlocal. Threadlocal is a thread-local variable class in Java that allows each thread to maintain its own independent object, typically through Threadlocal.set () to an object in the thread that the thread uses itself, and other threads that do not need access or access. For the threadlocal mechanism here do not do too much to repeat.
Here we set a looper for the thread, and then we look at the Looper constructor:
privateLooper(boolean quitAllowed) { new MessageQueue(quitAllowed); mThread = Thread.currentThread(); }
As you can see, in the constructor method, we create a new message queue and set up the current thread.
Next we create a handler, about handler we will analyze, in the end we call the Looper loop method for the message loop. Let's take a look at this method:
Public Static void Loop() {the//mylooper () method is to return the looper we just set by Sthreadlocal.get () FinalLooper me = Mylooper ();if(Me = =NULL) {Throw NewRuntimeException ("No Looper; Looper.prepare () wasn ' t called on the This thread. "); }FinalMessageQueue queue = Me.mqueue;//Ensure that the thread is consistent with the local process and that the identity token is loggedBinder.clearcallingidentity ();Final LongIdent = Binder.clearcallingidentity (); for(;;) {Message msg = Queue.next ();//May be blocked if(msg = =NULL) {//No message indicates that the message queue has exited. return; }//This must is in a local variable, with case a UI event sets the loggerPrinter logging = me.mlogging;if(Logging! =NULL) {Logging.println (">>>>> dispatching to"+ Msg.target +" "+ Msg.callback +": "+ msg.what); }//Distribute the messageMsg.target.dispatchMessage (msg);if(Logging! =NULL) {Logging.println ("<<<<< finished to"+ Msg.target +" "+ Msg.callback); }//Ensure that the thread does not crash during distribution Final LongNewident = 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.target.getClass (). GetName () +" "+ Msg.callback +"what="+ msg.what); } msg.recycle (); } }
As you can see, the loop method is primarily to remove messages from the message queue and distribute the message.
To summarize, Looper's work:
- Encapsulates a message queue,
- Use prepare to associate Looper with the thread that calls the Prepare method
- Distributing messages using the Loop function
Handler Analysis
Knowing the work of Looper, let's see how to send the message out. First, let's look at how it's constructed,
publicHandlerasync) { ...... mLooper = Looper.myLooper(); ifnull) { thrownew RuntimeException( "Can‘t create handler inside thread that has not called Looper.prepare()"); } mQueue = mLooper.mQueue; mCallback = callback; async; }
The parameterless construction method calls the This (null, flase) construction method, and you can see that there is a looper in the member variable of handler, first obtaining the looper of the thread that is currently creating handler, In addition, you can see that a message queue is also saved in handler that eventually points to the Looper message queue.
When we call the SendMessage method and send a message to Looper, let's take a look at this method, how the message is passed. The SendMessage method is eventually called to the Sendmessageattime method to:
Public Boolean Sendmessageattime(Message msg,LongUptimemillis) {MessageQueue queue = Mqueue;if(Queue = =NULL) {RuntimeException E =NewRuntimeException ( This+"Sendmessageattime () called with no Mqueue"); LOG.W ("Looper", E.getmessage (), E);return false; }returnEnqueuemessage (Queue, MSG, uptimemillis); }Private Boolean Enqueuemessage(MessageQueue queue, Message msg,LongUptimemillis) {Msg.target = This;if(masynchronous) {msg.setasynchronous (true); }returnQueue.enqueuemessage (msg, uptimemillis); }
As you can see, the SendMessage method eventually calls the Queue.enqueuemessage method to join the message queue in Looper.
In the above we see that our distribution message is called by the DispatchMessage method:
publicvoiddispatchMessage(Message msg) { ifnull) { handleCallback(msg); else { ifnull) { if (mCallback.handleMessage(msg)) { return; } } handleMessage(msg); } }
As you can see, DispatchMessage sets a priority mechanism for message handling:
- If the message comes with callback, it is handed to the callback processing of the message;
- If the handler set up the callback, then to handler callback processing;
- If both are not, call the Handlemessage method to process.
Synchronization issues for Handler and Looper
In the example above, we can see that handler and Looper are synchronous problems, and if handler is not created in Looperthread, a message is sent in the second thread, which throws a null pointer exception. In the above, I refer to the Android Handlerthread, using the wait/notifyall approach to solve the problem, in practice we can use the Handlerthread to complete.
FinalHandlerthread Mhandlerthread =NewHandlerthread ("Looperthread"); Mhandlerthread.start ();FinalHandler Mhandler =NewHandler (Mhandlerthread.getlooper ()) {@Override Public void Handlemessage(Message msg) {//TODO auto-generated method stub Super. Handlemessage (msg); LOG.W ("Looperthread","Handlemessage::thread ID---"+ Mhandlerthread.getid ()); } };NewThread () {@Override Public void Run() {//TODO auto-generated method stubMhandler.sendemptymessage (0); LOG.W (TAG,"Send message::thread ID---"+ getId ()); }}.start ();
We can look at the source code of Handlerthread:
@Override Public void Run() {Mtid = Process.mytid (); Looper.prepare ();synchronized( This) {Mlooper = Looper.mylooper (); Notifyall (); } process.setthreadpriority (mpriority); Onlooperprepared (); Looper.loop (); Mtid =-1; } PublicLooperGetlooper() {if(!isalive ()) {return NULL; }//If The thread has been started, wait until the Looper have been created. synchronized( This) { while(IsAlive () && Mlooper = =NULL) {Try{Wait (); }Catch(Interruptedexception e) { } } }returnMlooper; }
As you can see, this idea is consistent with what we have just shown in the example.
Android source code Analysis--handler and Looper mechanism detailed