Android Buffet Handler message mechanism full parsing (ii) MessageQueue queue management
- Android Buffet handler message mechanism fully resolves two MessageQueue queue management
- Add to Message Queue Enqueuemessage
- Remove a message from the queue next
- First paragraph
- Third paragraph
- Second paragraph
- Removing messages from a queue removemessages
- First while
- A second while
As for this queue, it is stated that the implementation of this queue is neither a subclass of collection nor a subclass of map, but a message itself. Because the message itself is a list node (see Obtain () and recycle () in message).
The member in the queue is the Message mMessages; queue, and the field directly points to the next message in the queue that needs to be processed.
Add to Message Queue
enqueueMessage()
To add a message to the queue in addition to providing the messages, you need to provide the trigger time when .
If the current queue is empty, you can mmessage=message directly. Otherwise, you will need to compare the when of each message in the queue with the when of the new message to determine the position of the new message in the queue.
First give the core source code (there are limitations)
message p = mmessages; if (p = = NULL | | when = = 0 | | when < P.when ) {Msg.next = p; Mmessages = msg;} else {message prev; for (;;) {prev = p; p = P.next ; if (p = = NULL | | when < P.when ) {break ; }} msg.next = p; Prev.next = msg;} if (Needwake) {Nativewake (mptr);}
First look at the new message needs to put on the team head situation: p == null || when == 0 || when < p.when . That is, the queue is empty, or the new message needs to be processed immediately, or the new message processing event is processed earlier than the team header message. Just let the new message point to the next current team header, so that mMessages you can point to the new message to complete the insert operation.
In addition to the above three cases you need to traverse the queue to determine the new message location, the following combination to illustrate.
Suppose the current message queue is as follows
Start traversal: P moves to the end of the queue, introducing prev to the previous element of P
Assuming that when the message referred to by P is later than the new message, the new message position is in the middle of Prev and P
Finally, call the native method to wake up (Linux epoll, interested in self-Baidu).
Extracting messages from a queue
next()
This part of the content is a bit high, please understand according to personal BPU (brainprocessunit) as appropriate.
First this method needs to be returned Message , so let's look at where there is return . (a total of three paragraphs, we finally see the second paragraph.) )
First paragraph
finallong ptr = mPtr;if0) { returnnull;}
If mptr is 0, NULL is returned. So what is Mptr? What does a value of 0 mean? The MessageQueue native method is called in the constructor method and returns the Mptr mPtr = nativeInit(); ; the value in the method is set to dispose() 0 and is mPtr = 0; called nativeDestroy() . And dispose() the method finalize() is called in. In addition, each time the use of Mptr calls the method of native, which is itself a long type, so it is inferred that it corresponds to a C + + pointer. It is therefore possible to determine, mPtr for a memory address, that the message queue is freed when it is 0. This makes it easy to understand why mPtr==0 the time to return null.
Third paragraph
You're not mistaken, the second paragraph is in the back.
if (mQuitting) { dispose(); returnnull;}
The meaning here is also obvious, when this message queue exits, the return empty. And the method is called before returning dispose() , which obviously means that the message queue will be freed.
Second paragraph
This part of the code is basically the next () method itself, but it is certain that the return statement here is return msg; . As you can see from the enqueuemessage () method, the Message object taken in this queue cannot be empty, so the return here is definitely not empty.
This can be a conclusion: if the next () method is empty, the message queue is exiting or will be released for recycling.
continue to see this next () , this code is a bit long, so first do a subtraction.
The first thing to subtract is pendingidlehandlercount , the local variable is initially 1, followed by the value midlehandlers.size (); . Midlehandlers Here is initially new arraylist<idlehandler> () , at Addidlehander () method to remove elements from the Removeidlehander () method. The Handeler we use does not implement the Idlehandler interface, so in the next () method The value of the Pendingidlehandlercount is either 0 or 1, so you can see that some of the code associated with the variable is OK, so that the code that does not affect the loop control is lost.
The second one to be reduced is binder.flushpendingcommands () This code see source description:
Flush any Binder commands pending in the current thread to the kernel driver. This can is useful to call before performing an operation the May block for a long time, to ensure the any pending OBJEC T references has been released in order to prevent the process from holding on to objects longer than it needs to.
It doesn't matter what this phrase is, but here's what you need to know: Binder.flushPendingCommands() The method is called to indicate that the code that follows may cause the thread to block. And then we'll lose the paragraph.
The third one to subtract is a log statement if (DEBUG) Log.v(TAG, "Returning message: " + msg);
The fourth is to reduce the "first paragraph" mentioned above to return NULL statements, but "the third paragraph" has to be kept.
Finally, the comment is eliminated to the code:
Message Next () {intNextpolltimeoutmillis =0; for(;;) {Nativepollonce (PTR, nextpolltimeoutmillis);synchronized( This) {Final Longnow = Systemclock.uptimemillis (); Message prevmsg =NULL; Message msg = mmessages;if(msg! =NULL&& Msg.target = =NULL) { Do{prevmsg = msg; msg = Msg.next; } while(msg! =NULL&&!msg.isasynchronous ()); }if(msg! =NULL) {if(Now < Msg.when) {Nextpolltimeoutmillis = (int) Math.min (Msg.when-now, Integer.max_value); }Else{mblocked =false;if(Prevmsg! =NULL) {prevmsg.next = Msg.next; }Else{mmessages = Msg.next; } Msg.next =NULL; Msg.markinuse ();returnMsg } }Else{Nextpolltimeoutmillis =-1; }if(mquitting) {Dispose ();return NULL; }if(Pendingidlehandlercount <=0) {//This variable is either 0 or 1 analyzed above.mblocked =true;Continue; }} Nextpolltimeoutmillis =0; }}
It's still a long time, but it can't be reduced any more. The general idea is to get the first synchronized message first. If it's when not later than the current time, return this message; otherwise calculate the current time to it's when still how long and save to nextPollTimeMills in, then call nativePollOnce() to delay wake up (Linux epoll, interested in self-Baidu), Wake up and then take the message as above, so loop. In the code, the pointer operation of the linked list occupies a certain length, the other logic is very clear, there is a sentence analysis.
Removing messages from a queue
removeMessages()
The method has 2 overloads, and there are removeCallbacksAndMessages() other ways to remove the message. But the code snippet is basically the same, here's an void removeMessages(Handler h, int what, Object object){} example of a method.
The complete source of the method is as follows
voidRemovemessages (Handler H,intWhat, ObjectObject) {if(H = =NULL) {return; } synchronized ( This) {Message p = mmessages;//Remove all messages at front. while(P! =NULL&& P.target = = h && p.what = = What && (Object==NULL|| P.obj = =Object) {Message n = p.next; Mmessages = n; P.recycleunchecked (); p = n; }//Remove all messages after front. while(P! =NULL) {Message n = p.next;if(n! =NULL) {if(N.target = = h && n.what = = What && (Object==NULL|| N.obj = =Object) {Message nn = n.next; N.recycleunchecked (); P.next = nn;Continue; }} p = N; } }}
The first thing to tell is whether handler is empty or not, and then the synchronization code snippet, with only two while loops. Why are there two of them? Learn the data structure linked lists are aware that the list of two types: the lead node and not the lead node. The two lists are traversed in a different way: in a linked list that does not take the lead, the first element needs to be processed separately before the subsequent part is used as a list of leading nodes to use while loop traversal. It can be seen that MessageQueue is not the lead node of the linked list, and there is a need to delete nodes during traversal, so it is not only the first element to deal with, but the first set of elements that meet the deletion criteria. A little dizzy, right, it doesn't matter, we start to fight the chart.
First while
Assume that you need to traverse the message queue.
In order for the first while to be executed, we assume that the first 3 elements conform to the removal criteria, that is, the first three message, respectively, and targe what the same as the obj specified one handler what object . First the first element satisfies the condition to do the following:
implementation of N=p.next;
Mmessage after moving;
Reclaims the element that P points to, that is, the first element.
Let P point to the new team head.
This is also the same as the initial queue state. We previously assumed that the team header has three elements that conform to the removal criteria, so we loop through the previous 4 Figure 2 side and then get the initial state of the queue, when the team head element does not meet the removal criteria so while terminating, while the new queue becomes the "lead node of the linked list", So the element that mmessage points to never has to be judged whether it satisfies the removal condition.
A second while
At this point the message queue status is as follows:
implementation of N=p.next;
Assuming that the element that n points to does not meet the removal criteria, only P and N are moved back, so that the element that P points to is always judged not to satisfy the removal condition. This part of the logic is very simple to give the graph is not to see the reader's IQ, now we assume that the element n points to meet the removal criteria, that is, the current queue as follows:
implementation of Nn=n.next;
Reclaim N-Pointing elements
implementation of P.NEXT=NN;
At this point the queue after P is also a chain list of leading nodes. You can continue with the while.
"Android Buffet" handler message mechanism full parsing (ii) MessageQueue queue management