Android messaging System
What the overall framework sees
In the Android messaging system, each thread has a looper,looper in which a Messagequeue,handler delivers a message to the queue Message,looper loops out of the message to be processed by handler. Overall is a producer-consumer model, and these four parts also form the Android messaging system.
Let's take a look at one of the simplest examples
//这段代码在某个Activity的onCreate中 Handler handler = new Handler(Looper.getMainLooper()); Message msg = Message.obtain(handler, new Runnable() { @Override public void run() { Toast.makeText(getApplicationContext,"I am a message",Toast.LENGTH_SHORT).show(); } }); handler.sendMessage(msg);
The effect is. POPs the I am a message in the current form. Of course, in fact, the effect is completely superfluous. But the Android messaging system is analyzed. It is a very easy and effective example.
Source Code Analysis Message
The
Message encapsulates what we often use, such as what, Arg1, arg2, obj, and so on, in addition to the target: a handler type, from the previous text that a message is finally handed to a handler run. This target stores the destination and callback of the message. A callback for a message. We pass Handler.post (new runnable{...}) The message sent. This runnable is saved as callback. The
first looks at getting the message:
public static Message obtain () {synchronized (Spoolsync) { if (SPool! = null ) {Message m = sPool; SPool = M.next; M.next = null ; spoolsize--; return m; }} return new Message (); } public static Message obtain (Handler H, Runnable callback) {Message m = obtain (); M.target = h; M.callback = callback; return m; }
In contrast to the most recent example, Message.obtain (Handler H, Runnable callback) first calls obtain to get a new Message object. The destination handler and callback function callback are then set. There are very many different obtain functions in the message class. Actually, it just encapsulates some assignment for us.
Look at the Message.obtain () method again. Spoolsync is a static lock used for static methods. Spool is a static message variable. In the acquisition of messages here, Android uses the enjoy meta mode for message messages that will be reused. No new object is created for each request. Instead, it maintains a list of messages, and takes a message from the list when there is spare. Create a new message only if it is not.
You can see that obtain only has a message from the linked list and a new message. Instead of the stored procedure to the linked list.
Storing this part depends on message.recycle ():
publicvoidrecycle() { clearForRecycle(); synchronized (sPoolSync) { if (sPoolSize < MAX_POOL_SIZE) { next = sPool; this; sPoolSize++; } } }
Recycling process. The head of the original list is first pointed to the next node of the currently recycled message. Then the linked-list-head pointer knows the current node is available. The whole operation is to add the message to the first of the list.
MessageQueue Message Queuing
MessageQueue is in the Looper. This can be seen from the Looper constructor:
privateLooper(boolean quitAllowed) { new MessageQueue(quitAllowed); mThread = Thread.currentThread(); }
For each MessageQueue, it is the message queue implemented by the linked list.
The first is the queue operation:
BooleanEnqueuemessage (Message msg,LongWhen) {if(Msg.isinuse ()) {Throw NewAndroidruntimeexception (msg +"This message was already in use."); }if(Msg.target = =NULL) {Throw NewAndroidruntimeexception ("Message must has a target."); }synchronized( This) {if(mquitting) {RuntimeException E =NewRuntimeException (Msg.target +"Sending message to a Handler on a dead thread"); LOG.W ("MessageQueue", E.getmessage (), E);return false; } Msg.when = when;//mmessages is the head pointer of the linked listMessage p = mmessages;BooleanNeedwake;if(p = =NULL|| when = =0|| When < P.when) {//Insert messages to the top of the queueMsg.next = p; Mmessages = msg; Needwake = mblocked; }Else{needwake = mblocked && P.target = =NULL&& msg.isasynchronous (); Message prev; for(;;) {prev = p; p = p.next;//When the end of the queue is reached, or the time of the current message is less than the time of a message in the queue if(p = =NULL|| When < P.when) { Break; }if(Needwake && p.isasynchronous ()) {Needwake =false; } }//Insert message into the middle of the list (including trailer)Msg.next = p;//Invariant:p = = Prev.nextPrev.next = msg; }//We can assume mptr! = 0 because mquitting is false. if(Needwake) {Nativewake (mptr); } }return true; }
Next operation. Includes removing and deleting a message.
Message Next () {intPendingidlehandlercount =-1;//-1 only during first iteration intNextpolltimeoutmillis =0; for(;;) {if(Nextpolltimeoutmillis! =0) {binder.flushpendingcommands (); }//Remove messages from native Layer message queueNativepollonce (Mptr, Nextpolltimeoutmillis);synchronized( This) {Final Longnow = Systemclock.uptimemillis (); Message prevmsg =NULL; Message msg = mmessages;if(msg! =NULL&& Msg.target = =NULL) {//Find non-asynchronous message or message queue trailer Do{prevmsg = msg; msg = Msg.next; } while(msg! =NULL&&!msg.isasynchronous ()); }if(msg! =NULL) {if(Now < Msg.when) {//Message not up to run time, Next loop suspend thread for some timeNextpolltimeoutmillis = (int) Math.min (Msg.when-now, Integer.max_value); }Else{//Get a messagemblocked =false;if(Prevmsg! =NULL) {prevmsg.next = Msg.next; }Else{mmessages = Msg.next; } Msg.next =NULL;if(false) LOG.V ("MessageQueue","Returning message:"+ msg); Msg.markinuse ();returnMsg } }Else{//No more messages.Nextpolltimeoutmillis =-1; }//Check exit flag bit if(mquitting) {Dispose ();return NULL; } ... } }
Handler
Handler's role is to put messages and process messages, taking on the work of producers and the work of some consumers.
First send a message via handler:
Public Final Boolean SendMessage(Message msg) {returnSendmessagedelayed (MSG,0); } 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); }
Through a layer of nesting, the real logic in the sendmessageattime, can see just run a bit of the queue operation. As a producer of the work is finished, the consumer part of the following should be combined with looper analysis.
In addition to the SendMessage method, the often used Handler.post method is also encapsulated as a message, the main process and similar above.
publicfinalbooleanpost(Runnable r) { return 0); }privatestaticgetPostMessage(Runnable r) { Message m = Message.obtain(); m.callback = r; return m; }
Looper
In the Looper class, an instance of Looper is obtained through threadlocal. Threadlocal will provide a copy for each thread, and each thread of the set and get methods gets the variable value that the scope belongs to only that thread. For the UI thread, Looper.preparemainlooper () is run to complete the initialization of Looper:
Public Static void Preparemainlooper() {Prepare (false); Synchronized (Looper.class) {if(Smainlooper! =NULL) {Throw NewIllegalStateException ("The main Looper has already been prepared."); } Smainlooper = Mylooper (); } }Private Static void Prepare(Boolean quitallowed) {if(sthreadlocal.Get() !=NULL) {Throw NewRuntimeException ("One Looper may be created per thread"); } sthreadlocal.Set(NewLooper (quitallowed)); }
The Looper.prepare () method sets the threadlocal of the current thread to a new Looper object. Preparemainlooper is to assign the Looper object of the current thread to the class variable smainlooper. This method is called in Activitythread and sets a global looper that is used by the UI thread.
The Looper loop method is the consumer's processing logic:
public static void loop() { final Looper me = myLooper(); if (me == null) { throw new RuntimeException("No Looper; Looper.prepare() wasn‘t called on this thread."); } final MessageQueue queue = me.mQueue; Binder.clearCallingIdentity(); final long ident = Binder.clearCallingIdentity(); //从Looper中获取MessageQueue,循环取出消息 for (;;) { Message msg = queue.next(); ... //将消息发送给目标处理。 msg.target.dispatchMessage(msg); ... //回收消息,把消息放在消息池中 msg.recycle(); } }
The main logic is very clear, the previous analysis Msg.target is a handler, represents the target of processing messages, through the command mode of the message to the corresponding handler processing.
Here's how to process messages in handler:
Public void DispatchMessage(Message msg) {if(Msg.callback! =NULL) {Handlecallback (msg); }Else{if(Mcallback! =NULL) {if(Mcallback.handlemessage (msg)) {return; }} handlemessage (msg); } }Private Static void Handlecallback(Message message) {Message.callback.run (); } Public void Handlemessage(Message msg) { }
Assuming that we are sending a message through the Handler.post method, run the logic in callback directly.
Otherwise, by implementing the callback interface callback, or by running handlemessage,handlemessage, which is our subclass override method.
Can see, although the logical part is we implement in handler. But the place to call is the looper thread. Because a looper binds a thread, we can also compare threads by comparing looper.
Summarize
By analyzing the source code, it is possible to know that Android can create a message queue for each thread through Looper, and the UI thread's looper is initialized before the activity starts.
So for our own defined thread. It is also very obvious to bind Looper.
Define the thread binding looper yourself. The most obvious advantage is the ability to achieve inter-thread communication, the same time because of the use of Message Queuing, but also the parallel to serial implementation of thread security. Look at a simple example:
new Thread (new Runnable () { @Override public void run () {Lo Oper.prepare (); Handlera = new Handler (Looper.mylooper ()) { @Override public void handlemessage (Message msg) {log.d ( "TAG" , msg.obj.toString ()); } }; Looper.loop (); }}). Start ();
A looper is created in the thread above, and a new handler is bound to the current looper, which enables the handler to add a message to Looper MessageQueue and then the Looper.loop to retrieve the message and run it.
MessageMessage(); "i am main thread"; handlerA.sendMessage(msg);
Get handler in the main thread or another thread and send a message, and finally be able to see the message being received and processed by the thread.
The target of MSG here is Handlera.
Note that assuming that the thread is working, you need to call Looper.quit (). Otherwise, the thread will not end because the Looper has been circulating.
Finally, through the above analysis, the flowchart can be drawn more carefully:
Android messaging System handler, MessageQueue, Looper source code Learning