We first look at the creation of Handlerthread,
publicclass HandlerThread extends Thread { @Override publicvoidrun() { mTid = Process.myTid(); Looper.prepare(); synchronized (this) { mLooper = Looper.myLooper(); notifyAll(); } Process.setThreadPriority(mPriority); onLooperPrepared(); Looper.loop(); mTid = -1; }}
Call Looper.prepare () to prepare a looper for the current ready-made
publicstaticvoidprepare() { prepare(true);}privatestaticvoidprepare(boolean quitAllowed) { if (sThreadLocal.getnull) { thrownew RuntimeException("Only one Looper may be created per thread"); } sThreadLocal.set(new Looper(quitAllowed));}
Use sthreadlocal to ensure that each thread has its own Looper object
Finally, inside the Handlerthread Run method, enter the Looper.loop method
Public Static void Loop() {FinalLooper me = Mylooper ();if(Me = =NULL) {Throw NewRuntimeException ("No Looper; Looper.prepare () wasn ' t called on the This thread. "); }FinalMessageQueue queue = Me.mqueue;//Make sure the identity of this thread is the the local process, //And keep track of the What, identity token actually is.Binder.clearcallingidentity ();Final LongIdent = Binder.clearcallingidentity (); for(;;) {Message msg = Queue.next ();//might block if(msg = =NULL) {//No message indicates that the message queue is quitting. 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); } msg.target.dispatchMessage (msg);if(Logging! =NULL) {Logging.println ("<<<<< finished to"+ Msg.target +" "+ Msg.callback); }//Make sure that during the course of dispatching the //identity of the thread wasn ' t corrupted. 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.recycleunchecked (); }}
You can see that this is a dead loop, and each time you take a message out of the MessageQueue and take it out for processing and dispatch.
The role of binder.clearcallingidentity () I guess is used to clear the remote caller's ID, because the remote IPC thread may not have some permissions, after the ID is cleared, you can do some permission operation on behalf of the remote process.
Let's look at the next method of MessageQueue:
Message Next () {//Return hereifThe messageLoophas already quit andbeen disposed.//This can happenifThe application tries to restart a looper after quit//which is notSupported. Final long ptr = mptr;if(ptr = =0) {return NULL; } int pendingidlehandlercount =-1;//-1Only during first iteration int nextpolltimeoutmillis =0; for(;;) {if(Nextpolltimeoutmillis! =0) {binder.flushpendingcommands (); } nativepollonce (PTR, nextpolltimeoutmillis); Synchronized ( This) {//Try to retrieve the next message. ReturnifFound. Final Long now = Systemclock.uptimemillis (); Message prevmsg =NULL; Message msg = mmessages;if(msg! =NULL&& Msg.target = =NULL) {//Stalled bya barrier. Find the next asynchronous messageinchThe queue. Do{prevmsg = msg; msg = Msg.next; } while(msg! =NULL&&!msg.isasynchronous ()); }if(msg! =NULL) {if(Now < Msg.) when) {//Next message is notReady. Set a timeout to wake up whenIt isReady. Nextpolltimeoutmillis = (int) math.min (msg. when-Now, Integer.max_value); }Else{//Got a message. mblocked =false;if(Prevmsg! =NULL) {prevmsg.next = Msg.next; }Else{mmessages = Msg.next; } Msg.next =NULL;if(false) LOG.V ("MessageQueue","Returning message:"+ msg);returnMsg } }Else{//No more messages. Nextpolltimeoutmillis =-1; }//Process the Quit message now, all pending messages has been handled.if(mquitting) {Dispose ();return NULL; }//If First time Idle, ThenGet the number ofIdlers to run.//Idle handles only runifThe queue isEmptyor ifThe first message// inchThe queue (possibly a barrier) isDue to be handledinchThe future.if(Pendingidlehandlercount <0&& (Mmessages = =NULL|| Now < Mmessages. when) {Pendingidlehandlercount = Midlehandlers.size (); }if(Pendingidlehandlercount <=0) {//No idle handlers to run. Loop andWait some more. mblocked =true;Continue; }if(Mpendingidlehandlers = =NULL) {mpendingidlehandlers =NewIdlehandler[math.max (Pendingidlehandlercount,4)]; } mpendingidlehandlers = Midlehandlers.toarray (mpendingidlehandlers); }//Run the idle handlers.//We only ever reach Thiscode block during the first iteration. for(int i =0; i < Pendingidlehandlercount; i++) {final Idlehandler idler = mpendingidlehandlers[i]; Mpendingidlehandlers[i] =NULL;//Release the reference to the handler Boolean keep =false;Try{keep = Idler.queueidle (); }Catch(Throwable t) {LOG.WTF ("MessageQueue","Idlehandler threw exception", t); }if(!keep) {Synchronized ( This) {Midlehandlers.remove (idler); } } }//Reset the idle handler count to0So we Do notRun them again. Pendingidlehandlercount =0;//While calling an idle handler, aNewMessage could has been delivered//So go back andLook again forA pending message without waiting. Nextpolltimeoutmillis =0; }}
The function of Nativepollonce (PTR, Nextpolltimeoutmillis) is to invoke the Jni method to perform a poll operation if the current message queue is empty (or empty except for some timed message that is not yet present). This method returns only if the message queue has a new message (or the time-out expires) and continues processing.
At the end of the loop, there is a concept called Idlehandler, which is called when the message queue is not currently available for processing, which means that when the idlehandler is called, the Idlehandler This means that Message Queuing is currently in a state where no messages can be processed, and the app can add Idlehandler using the MessageQueue Addidlehandler method.
Android MessageQueue and Loop analysis