Android is a message driven, message driven several elements:
The message says: Message
Message queue: MessageQueue
The news cycle, remove the message processing for circulation: Looper
Message processing, message loop out messages from the message queue should be carried out after the processing of messages: Handler
Usually we use most often is Message and Handler, if you use HandlerThread or similar HandlerThread things may come into contact with Looper, while MessageQueue is used inside Looper, for the standard SDK, we are unable to instantiate and use (constructor is package visibility).
We usually come into contact with Looper, Message, Handler are realized by JAVA, Android for Linux based systems, the bottom with a C, C++, and NDK exist, how could the message driven model only exists in the JAVA layer, in fact, there are corresponding to the Java layer such as Looper, MessageQueue in the Native layer.
In the red mark of the two, first call prepare to initialize the MessageQueue and Looper, and then call the loop enter the message loop. First look at the Looper.prepare.
publicstaticvoidprepare(){prepare(true);}privatestaticvoidprepare(booleanquitAllowed){if(sThreadLocal.get()!=null){thrownewRuntimeException("Only one Looper may be created per thread");}sThreadLocal.set(newLooper(quitAllowed));}
Overloaded functions, quitAllowed default to true, from the name can be seen is whether the message loop can exit, the default is to exit, the Main thread (UI thread) initialization message loop is called prepareMainLooper, pass is false. The use of ThreadLocal, each thread can initialize a Looper.
In the Looper initialization, a MessageQueue object created saved in the mQueue member. The MessageQueue constructor is package visibility, so we cannot be used directly, while the MessageQueue initialization is called the nativeInit, which is a Native method:
staticvoidandroid_os_MessageQueue_nativeInit(JNIEnvenv,jobjectobj){NativeMessageQueuenativeMessageQueue=newNativeMessageQueue();if(!nativeMessageQueue){jniThrowRuntimeException(env,"Unable to allocate native queue");return;}nativeMessageQueue–>incStrong(env);android_os_MessageQueue_setNativeMessageQueue(env,obj,nativeMessageQueue);}staticvoidandroid_os_MessageQueue_setNativeMessageQueue(JNIEnvenv,jobjectmessageQueueObj,NativeMessageQueuenativeMessageQueue){env–>SetIntField(messageQueueObj,gMessageQueueClassInfo.mPtr,reinterpret_cast<jint>(nativeMessageQueue));}
In nativeInit, the new object of a Native layer MessageQueue, and the address stored in the Java layer of the MessageQueue member of the mPtr, there are a lot of implementation of Android, a class has been implemented in Java layer and Native layer, the GetFieldID and SetIntField JNI saved Native layer class an address to the Java layer for an instance of the class mPtr members, such as Parcel.
Gets a Native layer of the Looper object in the constructor of the NativeMessageQueue, Native layer Looper also use thread local storage, note that new Looper introduced parameter false.
Looper::Looper(boolallowNonCallbacks):mAllowNonCallbacks(allowNonCallbacks),mSendingMessage(false),mResponseIndex(0),mNextMessageUptime(LLONG_MAX){intwakeFds[2];intresult=pipe(wakeFds);LOG_ALWAYS_FATAL_IF(result!=0,"Could not create wake pipe. errno=%d",errno);mWakeReadPipeFd=wakeFds[0];mWakeWritePipeFd=wakeFds[1];result=fcntl(mWakeReadPipeFd,F_SETFL,O_NONBLOCK);LOG_ALWAYS_FATAL_IF(result!=0,"Could not make wake read pipe non-blocking. errno=%d",errno);result=fcntl(mWakeWritePipeFd,F_SETFL,O_NONBLOCK);LOG_ALWAYS_FATAL_IF(result!=0,"Could not make wake write pipe non-blocking. errno=%d",errno);// Allocate the epoll instance and register the wake pipe.mEpollFd=epoll_create(EPOLL_SIZE_HINT);LOG_ALWAYS_FATAL_IF(mEpollFd<0,"Could not create epoll instance. errno=%d",errno);structepoll_eventeventItem;memset(&eventItem,0,sizeof(epoll_event));// zero out unused members of data field unioneventItem.events=EPOLLIN;eventItem.data.fd=mWakeReadPipeFd;result=epoll_ctl(mEpollFd,EPOLL_CTL_ADD,mWakeReadPipeFd,&eventItem);LOG_ALWAYS_FATAL_IF(result!=0,"Could not add wake read pipe to epoll instance. errno=%d",errno);}
Native layer in Looper using epoll. Initializes a pipeline, mWakeWritePipeFd and mWakeReadPipeFd were preserved to write and read end of pipe end, and monitor the read end of the EPOLLIN event. Note the initializer list of values, the value of mAllowNonCallbacks for false.
What is mAllowNonCallback? The use of epoll to monitor mWakeReadPipeFd events? In fact, Native Looper can not only monitor the descriptors, Looper also provides the addFd method:
fd said to monitor descriptor. ident said to monitor event identification, Value must be >=0 or be ALOOPER_POLL_BACK(-2), event said to monitor events, callback is a callback function when the event occurs, This is the role of mAllowNonCallbacks, When the mAllowNonCallbacks is true allows callback to NULL, ident in pollOnce as a result of return, Otherwise, do not allow the callback is empty, When the callback is not NULL, The value of ident will be ignored. Or just look at the code easy to understand:
intLooper::addFd(intfd,intident,intevents,constsp<LooperCallback>&callback,void*data){#if DEBUG_CALLBACKSALOGD("%p ~ addFd – fd=%d, ident=%d, events=0x%x, callback=%p, data=%p",this,fd,ident,events,callback.get(),data);#endifif(!callback.get()){if(!mAllowNonCallbacks){ALOGE("Invalid attempt to set NULL callback but not allowed for this looper.");return–1;}if(ident<0){ALOGE("Invalid attempt to set NULL callback with ident <0.");return–1;}}else{ident=ALOOPER_POLL_CALLBACK;}intepollEvents=0;if(events&ALOOPER_EVENT_INPUT)epollEvents|=EPOLLIN;if(events&ALOOPER_EVENT_OUTPUT)epollEvents|=EPOLLOUT;{// acquire lockAutoMutex_l(mLock);Requestrequest;request.fd=fd;request.ident=ident;request.callback=callback;request.data=data;structepoll_eventeventItem;memset(&eventItem,0,sizeof(epoll_event));// zero out unused members of data field unioneventItem.events=epollEvents;eventItem.data.fd=fd;ssize_trequestIndex=mRequests.indexOfKey(fd);if(requestIndex<0){intepollResult=epoll_ctl(mEpollFd,EPOLL_CTL_ADD,fd,&eventItem);if(epollResult<0){ALOGE("Error adding epoll events for fd %d, errno=%d",fd,errno);return–1;}mRequests.add(fd,request);}else{intepollResult=epoll_ctl(mEpollFd,EPOLL_CTL_MOD,fd,&eventItem);if(epollResult<0){ALOGE("Error modifying epoll events for fd %d, errno=%d",fd,errno);return–1;}mRequests.replaceValueAt(requestIndex,request);}}// release lockreturn1;}
If the callback is empty will check to see whether to allow mAllowNonCallbacks callback is empty, if the callback is empty will determine whether the ident > =0. If the callback is not empty the ident value of ALOOPER_POLL_CALLBACK, no matter in what value is.
Then the parameters of incoming values encapsulated into a Request structure, and the descriptor for the key to save to a KeyedVector mRequests, and then through the epoll_ctl to add or replace (if the descriptor before calling the addFD add monitoring) on this descriptor wiretapping.
Through the Looper.prepare initialized the message queue can be called after the Looper.loop enter the message loop, then we can send message to the message queue, message loop will remove messages are processed, before seeing the message processing, look at the news is how to be added to the message queue.
In the Java layer, the Message class represents a message object, to send a message must first obtain a message object, the constructor of the Message class is public, but does not recommend direct new Message, Message stored in a cache of the message pool, we can get a message from the buffer pool to use obtain, Message after using the system calls the recycle recovery, if they new a lot of Message, after each use system into the buffer pool, will occupy a lot of memory, as shown below:
The internal Message achieve a list by next members, such as the sPool to cache a list of Messages.The message object get how to send it, as we all know is by Handler post, sendMessage and other methods, but these methods are ultimately sendMessageAtTime with a method call:
publicbooleansendMessageAtTime(Messagemsg,longuptimeMillis){MessageQueuequeue=mQueue;if(queue==null){RuntimeExceptione=newRuntimeException(this+" sendMessageAtTime() called with no mQueue");Log.w("Looper",e.getMessage(),e);returnfalse;}returnenqueueMessage(queue,msg,uptimeMillis);}
sendMessageAtTime access to the message queue and then call the enqueueMessage method, the mQueue is obtained from the message queue associated with the HandlerLooper.
enqueueMessage message target is set to the current handler, and then call the MessageQueueenqueueMessage, before calling queue.enqueueMessage judge mAsynchronous, from the name is asynchronous message means, to understand the role of Asynchronous, need to understand a concept Barrier.
Barrier is what meaning, from the name is an interceptor, the interceptor behind the news is temporarily unable to perform, until this interceptor is removed, the MessageQueue has a function called enqueueSyncBarier can add a Barrier.
intenqueueSyncBarrier(longwhen){// Enqueue a new sync barrier token.// We don't need to wake the queue because the purpose of a barrier is to stall it.synchronized(this){finalinttoken=mNextBarrierToken++;finalMessagemsg=Message.obtain();msg.arg1=token;Messageprev=null;Messagep=mMessages;if(when!=0){while(p!=null&&p.when<=when){prev=p;p=p.next;}}if(prev!=null){// invariant: p == prev.nextmsg.next=p;prev.next=msg;}else{msg.next=p;mMessages=msg;}returntoken;}}
In enqueueSyncBarrier, Obtain a Message, And set msg.arg1=token, Token is only one of each call to enqueueSyncBarrier since the increase of int value, Objective is to return a token only each call to enqueueSyncBarrier, The Message also need to set the execution time, And then inserted into the message queue, Special is that the Message is not set to target, Msg.target null.
Enter the message loop will keep the news from the MessageQueue in the implementation, call the next function MessageQueue, in which there was a:
Messagemsg=mMessages;if(msg!=null&&msg.target==null){// Stalled by a barrier. Find the next asynchronous message in the queue.do{prevMsg=msg;msg=msg.next;}while(msg!=null&&!msg.isAsynchronous());}
If the head of the queue for the message target null says it is a Barrier, because there are only two ways to add mMessages news, one is enqueueMessage, another is enqueueBarrier, and enqueueMessage if msg.target null is a direct throw an exception, will see the back.
Asynchronous message call is actually the case, we can through the enqueueBarrier to the message queue insert a Barrier, then the implementation of synchronization message queue in the Barrier time will be the Barrier intercept cannot execute until we call removeBarrier, remove the Barrier, and asynchronous message has no effect, the default is the synchronous message the message, unless we call the MessagesetAsynchronous, this method is hidden. Only in the Handler initialization parameter to the Handler specified by the message sent is asynchronous, so that it will be in the HandlerenqueueMessage called MessagesetAsynchronous setup messages are asynchronous, from the above Handler.enqueueMessage code can be seen in the.
The so-called asynchronous message, there is only one, is in setting the Barrier still can be not affected by Barrier is normal, if not set Barrier, asynchronous message is no different from the synchronous message, can pass the removeSyncBarrier to remove the Barrier:
voidremoveSyncBarrier(inttoken){// Remove a sync barrier token from the queue.// If the queue is no longer stalled by a barrier then wake it.finalbooleanneedWake;synchronized(this){Messageprev=null;Messagep=mMessages;while(p!=null&&(p.target!=null||p.arg1!=token)){prev=p;p=p.next;}if(p==null){thrownewIllegalStateException("The specified message queue synchronization "+" barrier token has not been posted or has already been removed.");}if(prev!=null){prev.next=p.next;needWake=false;}else{mMessages=p.next;needWake=mMessages==null||mMessages.target!=null;}p.recycle();}if(needWake){nativeWake(mPtr);}}
The return value of parameter token is enqueueSyncBarrier, if not non-existent exception will be thrown calls the specified token.
finalbooleanenqueueMessage(Messagemsg,longwhen){if(msg.isInUse()){thrownewAndroidRuntimeException(msg+" This message is already in use.");//red mark}if(msg.target==null){thrownewAndroidRuntimeException("Message must have a target.");}booleanneedWake;synchronized(this){if(mQuiting){RuntimeExceptione=newRuntimeException(msg.target+" sending message to a Handler on a dead thread");Log.w("MessageQueue",e.getMessage(),e);returnfalse;}msg.when=when;Messagep=mMessages;if(p==null||when==0||when<p.when){// New head, wake up the event queue if blocked.msg.next=p;mMessages=msg;needWake=mBlocked;}else{// Inserted within the middle of the queue. Usually we don't have to wake// up the event queue unless there is a barrier at the head of the queue// and the message is the earliest asynchronous message in the queue.needWake=mBlocked&&p.target==null&&msg.isAsynchronous();Messageprev;for(;;){prev=p;p=p.next;if(p==null||when<p.when){break;}if(needWake&&p.isAsynchronous()){needWake=false;}}msg.next=p;// invariant: p == prev.nextprev.next=msg;}}if(needWake){nativeWake(mPtr);}returntrue;}
When the msg.target null is directly thrown exception.The first judgment in enqueueMessage, if the current message queue is empty, the execution time of when or add a new message is 0, or add a new message than the execution time of the message queue head message execution time is early, the message is added to the message queue head (message queue according to the time sequence), or to find a suitable location to the current message is added to the message queue.
voidLooper::sendMessageAtTime(nsecs_tuptime,constsp<MessageHandler>&handler,constMessage&message){#if DEBUG_CALLBACKSALOGD("%p ~ sendMessageAtTime – uptime=%lld, handler=%p, what=%d",this,uptime,handler.get(),message.what);#endifsize_ti=0;{// acquire lockAutoMutex_l(mLock);size_tmessageCount=mMessageEnvelopes.size();while(i<messageCount&&uptime>=mMessageEnvelopes.itemAt(i).uptime){i+=1;}MessageEnvelopemessageEnvelope(uptime,handler,message);mMessageEnvelopes.insertAt(messageEnvelope,i,1);// Optimization: If the Looper is currently sending a message, then we can skip// the call to wake() because the next thing the Looper will do after processing// messages is to decide when the next wakeup time should be. In fact, it does// not even matter whether this code is running on the Looper thread.if(mSendingMessage){return;}}// release lock// Wake the poll loop only when we enqueue a new message at the head.if(i==0){wake();}}
Native Message only one int type what field is used to distinguish between different message, SendMessageAtTimeMessage is specified, Message to the execution time of when, And processing the message Handler: MessageHandler, Then use the MessageEnvelope package of time, MessageHandler and Message, Native messages are saved to the mMessageEnvelopes, MMessageEnvelopes is a Vector<MessageEnvelope>. The Native message is also according to the time sequence, and the Java layer information is stored separately in the two cohort.
publicstaticvoidloop(){finalLooperme=myLooper();if(me==null){thrownewRuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");}finalMessageQueuequeue=me.mQueue;// Make sure the identity of this thread is that of the local process,// and keep track of what that identity token actually is.Binder.clearCallingIdentity();finallongident=Binder.clearCallingIdentity();for(;;){Messagemsg=queue.next();// might blockif(msg==null){// No message indicates that the message queue is quitting.return;}// This must be in a local variable, in case a UI event sets the loggerPrinterlogging=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.finallongnewIdent=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();}}
When loop took a Message from the MessageQueue, call msg.target.dispatchMessage (MSG), handler and message associated target is to send message, so that the familiar dispatchMessage calls, Message was treated by recycle. When queue.next returns null will exit the news cycle, then look at MessageQueue.next is how to remove the message, and returns null when.
finalMessagenext(){intpendingIdleHandlerCount=–1;// -1 only during first iterationintnextPollTimeoutMillis=0;for(;;){if(nextPollTimeoutMillis!=0){Binder.flushPendingCommands();}nativePollOnce(mPtr,nextPollTimeoutMillis);synchronized(this){if(mQuiting){returnnull;}// Try to retrieve the next message. Return if found.finallongnow=SystemClock.uptimeMillis();MessageprevMsg=null;Messagemsg=mMessages;if(msg!=null&&msg.target==null){// Stalled by a barrier. Find the next asynchronous message in the queue.do{prevMsg=msg;msg=msg.next;}while(msg!=null&&!msg.isAsynchronous());}if(msg!=null){if(now<msg.when){// Next message is not ready. Set a timeout to wake up when it is ready.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);msg.markInUse();returnmsg;}}else{// No more messages.nextPollTimeoutMillis=–1;}// If first time idle, then get the number of idlers to run.// Idle handles only run if the queue is empty or if the first message// in the queue (possibly a barrier) is due to be handled in the future.if(pendingIdleHandlerCount<0&&(mMessages==null||now<mMessages.when)){pendingIdleHandlerCount=mIdleHandlers.size();}if(pendingIdleHandlerCount<=0){// No idle handlers to run. Loop and wait 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 this code block during the first iteration.for(inti=0;i<pendingIdleHandlerCount;i++){finalIdleHandleridler=mPendingIdleHandlers[i];mPendingIdleHandlers[i]=null;// release the reference to the handlerbooleankeep=false;try{keep=idler.queueIdle();}catch(Throwablet){Log.wtf("MessageQueue","IdleHandler threw exception",t);}if(!keep){synchronized(this){mIdleHandlers.remove(idler);}}}// Reset the idle handler count to 0 so we do not run them again.pendingIdleHandlerCount=0;// While calling an idle handler, a new message could have been delivered// so go back and look again for a pending message without waiting.nextPollTimeoutMillis=0;}}
MessageQueue.next will be the first to call nativePollOnce, then if mQuiting true returns null, Looper will exit the message loop.
The next head of the news from the message queue, If the head message is Barrier (target==null) on the next traversal to find the first asynchronous message, The next test access to the message (message queue message or the first asynchronous message), If NULL said no message to perform, Set nextPollTimeoutMillis = -1; otherwise detect this news to execution time., If the execution time of the markInUse and the message from the message queue to remove, And then returns from next to loop; otherwise, set nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE), That is the nearest to perform message also need how long, Both the current message queue no news can perform (set Barrier and no asynchronous message or the message queue is empty) or the head of the queue of the message not to execution time, Will execute the code behind the, To see if there is no set IdleHandler, If there is to run IdleHandler, When the IdleHandler is executing will set nextPollTimeoutMillis = 0.
First look at the nativePollOnce, native method, called JNI, and finally transferred to the Native Looper:: pollOnce, and from Java layer transfer in nextPollTimeMillis, namely the execution time of Java layer in the message queue to recent news how long execution time.
intLooper::pollInner(inttimeoutMillis){#if DEBUG_POLL_AND_WAKEALOGD("%p ~ pollOnce – waiting: timeoutMillis=%d",this,timeoutMillis);#endif// Adjust the timeout based on when the next message is due.if(timeoutMillis!=0&&mNextMessageUptime!=LLONG_MAX){nsecs_tnow=systemTime(SYSTEM_TIME_MONOTONIC);intmessageTimeoutMillis=toMillisecondTimeoutDelay(now,mNextMessageUptime);if(messageTimeoutMillis>=0&&(timeoutMillis<0||messageTimeoutMillis<timeoutMillis)){timeoutMillis=messageTimeoutMillis;}#if DEBUG_POLL_AND_WAKEALOGD("%p ~ pollOnce – next message in %lldns, adjusted timeout: timeoutMillis=%d",this,mNextMessageUptime–now,timeoutMillis);#endif}// Poll.intresult=ALOOPER_POLL_WAKE;mResponses.clear();mResponseIndex=0;structepoll_eventeventItems[EPOLL_MAX_EVENTS];inteventCount=epoll_wait(mEpollFd,eventItems,EPOLL_MAX_EVENTS,timeoutMillis);// Acquire lock.mLock.lock();// Check for poll error.if(eventCount<0){if(errno==EINTR){gotoDone;}ALOGW("Poll failed with an unexpected error, errno=%d",errno);result=ALOOPER_POLL_ERROR;gotoDone;}// Check for poll timeout.if(eventCount==0){#if DEBUG_POLL_AND_WAKEALOGD("%p ~ pollOnce – timeout",this);#endifresult=ALOOPER_POLL_TIMEOUT;gotoDone;}// Handle all events.#if DEBUG_POLL_AND_WAKEALOGD("%p ~ pollOnce – handling events from %d fds",this,eventCount);#endiffor(inti=0;i<eventCount;i++){intfd=eventItems[i].data.fd;uint32_tepollEvents=eventItems[i].events;if(fd==mWakeReadPipeFd){if(epollEvents&EPOLLIN){awoken();}else{ALOGW("Ignoring unexpected epoll events 0x%x on wake read pipe.",epollEvents);}}else{ssize_trequestIndex=mRequests.indexOfKey(fd);if(requestIndex>=0){intevents=0;if(epollEvents&EPOLLIN)events|=ALOOPER_EVENT_INPUT;if(epollEvents&EPOLLOUT)events|=ALOOPER_EVENT_OUTPUT;if(epollEvents&EPOLLERR)events|=ALOOPER_EVENT_ERROR;if(epollEvents&EPOLLHUP)events|=ALOOPER_EVENT_HANGUP;pushResponse(events,mRequests.valueAt(requestIndex));}else{ALOGW("Ignoring unexpected epoll events 0x%x on fd %d that is ""no longer registered.",epollEvents,fd);}}}Done:;// Invoke pending message callbacks.mNextMessageUptime=LLONG_MAX;while(mMessageEnvelopes.size()!=0){nsecs_tnow=systemTime(SYSTEM_TIME_MONOTONIC);constMessageEnvelope&messageEnvelope=mMessageEnvelopes.itemAt(0);if(messageEnvelope.uptime<=now){// Remove the envelope from the list.// We keep a strong reference to the handler until the call to handleMessage// finishes. Then we drop it so that the handler can be deleted before// we reacquire our lock.{// obtain handlersp<MessageHandler>handler=messageEnvelope.handler;Messagemessage=messageEnvelope.message;mMessageEnvelopes.removeAt(0);mSendingMessage=true;mLock.unlock();#if DEBUG_POLL_AND_WAKE || DEBUG_CALLBACKSALOGD("%p ~ pollOnce – sending message: handler=%p, what=%d",this,handler.get(),message.what);#endifhandler–>handleMessage(message);}// release handlermLock.lock();mSendingMessage=false;result=ALOOPER_POLL_CALLBACK;}else{// The last message left at the head of the queue determines the next wakeup time.mNextMessageUptime=messageEnvelope.uptime;break;}}// Release lock.mLock.unlock();// Invoke all response callbacks.for(size_ti=0;i<mResponses.size();i++){Response&response=mResponses.editItemAt(i);if(response.request.ident==ALOOPER_POLL_CALLBACK){intfd=response.request.fd;intevents=response.events;void*data=response.request.data;#if DEBUG_POLL_AND_WAKE || DEBUG_CALLBACKSALOGD("%p ~ pollOnce – invoking fd event callback %p: fd=%d, events=0x%x, data=%p",this,response.request.callback.get(),fd,events,data);#endifintcallbackResult=response.request.callback–>handleEvent(fd,events,data);if(callbackResult==0){removeFd(fd);}// Clear the callback reference in the response structure promptly because we// will not clear the response vector itself until the next poll.response.request.callback.clear();result=ALOOPER_POLL_CALLBACK;}}returnresult;}
Java messages are stored in the Java layer of the MessageQueue member of the mMessages, Native messages are stored in the Native LoopermMessageEnvelopes, it can be said that there are two message queue, and is arranged by time. TimeOutMillis said Java layer next to perform the news how long execution, mNextMessageUpdate said Native layer next to perform the news how long, if timeOutMillis is 0, epoll_wait does not set the TimeOut returned directly; if -1 Java layer without message directly with the Native time out; minimum or pollInner take the two one as the timeOut call epoll_wait. When epoll_wait returns may have the following conditions:
Error return
Time out
The normal return, there are events generated descriptors.
If the former is directly goto DONE two.
Otherwise, FD has happened, If the mWakeReadPipeFd EPOLLIN event awoken is called, If not mWakeReadPipeFd, It is through the addFD add FD, In addFD, to FD and events., callback,The data package into the Request object, Taking FD as the key stored in the KeyedVector mRequests, So here in FD to obtain association in addFD Request, And along with the events by adding pushResonsemResonse queue(Vector), Resonse is only for events and Request package. If an error of epoll_wait or timeout, there is no event descriptors, not the implementation of this code, so the direct goto DONE.
Enter DONE next, remove Native message headers from mMessageEnvelopes, if arrived at execution time handleMessage is called to handle it internally stored MessageeHandler and removed from the Native message queue, set result to ALOOPER_POLL_CALLBACK, otherwise the calculation of mNextMessageUptime said Native message queue a message to execution time. If not to the head message execution time may be Java layer message queuing message execution time is less than Native layer message queue message execution time, arrived at the execution time of epoll_wait TimeOut Java message, or add by addFd descriptor has occurred in epoll_wait returns, or epoll_wait error return. The Native message is not Barrier and Asynchronous.
Last, Through the mResponses (just by pushResponse put into it), If response.request.ident = = ALOOPER_POLL_CALLBACK, We call the registration of callback handleEvent (FD, events, data) processing, And then removed from the mResonses queue, After the traversal., MResponses retention and are ident> =0 and callback NULL. In the NativeMessageQueue initialization Looper into mAllowNonCallbacks false, so the treatment after mResponses must be empty.
Then return to pollOnce. PollOnce is a for cycle, The pollInner handles all response.request.ident==ALOOPER_POLL_CALLBACK Response, In second into the for cycle if the mResponses is not empty to find ident> 0 Response, The ident as the value returned by the function call pollOnce’s own processing, Here we are called in NativeMessageQueue Loope pollOnce, No return processing, But mAllowNonCallbacks false is not likely to enter the cycle. The return value of pollInner may not be 0, or can only be negative, so pollOnce for cycle will only be executed two times, then returned in second times.Native Looper can be used alone, also has a prepare function, then the mAllowNonCallbakcs value may be true, pollOnce in mResponses makes sense.
In the Native Looper constructor, The pipe opened a pipeline, Combined use of mWakeReadPipeFd and mWakeWritePipeFd were preserved the read and write end of pipe end, Then use the epoll_ctl (mEpollFd, EPOLL_CTL_ADD, mWakeReadPipeFd, & eventItem) monitored the read end of EPOLLIN events, In the pollInner by epoll_wait (mEpollFd, eventItems, EPOLL_MAX_EVENTS, timeoutMillis) read event, When writing to mWakeWritePipeFd, And at what time to read mWakeReadPipeFd?
In Looper.cpp we can be found below two functions:
voidLooper::wake(){#if DEBUG_POLL_AND_WAKEALOGD("%p ~ wake",this);#endifssize_tnWrite;do{nWrite=write(mWakeWritePipeFd,"W",1);}while(nWrite==–1&&errno==EINTR);if(nWrite!=1){if(errno!=EAGAIN){ALOGW("Could not write wake signal, errno=%d",errno);}}}voidLooper::awoken(){#if DEBUG_POLL_AND_WAKEALOGD("%p ~ awoken",this);#endifcharbuffer[16];ssize_tnRead;do{nRead=read(mWakeReadPipeFd,buffer,sizeof(buffer));}while((nRead==–1&&errno==EINTR)||nRead==sizeof(buffer));}
The wake function to write a &ldquo to mWakeWritePipeFdW character, awoken read from mWakeReadPipeFd, to mWakeWritePipeFd to write data to pollInner in the epoll_wait you can listen to the event returns. The pollInner can also see if the mWakeReadPipeFd EPOLLIN event is called the awoken consumed character written back processing.
When calling wake? Just find a place of call on the line, Look at the Looper.cpp, In the sendMessageAtTime when sending Native Message, The calculation should be inserted according to the execution time for mMessageEnvelopes to send Message, If it is in the head is inserted into the, We call the wake wake up epoll_wait, Because in the pollInner according to the execution time and the Native layer message queue head message execution time Java layer message queue header calculated a timeout, If the new message is inserted in the head, The execution time of at least the two messages in a before, So you should wake up epoll_wait, Epoll_wait returns, Check the Native message queue, To see whether the head message is just inserted message to the execution time, To execute, Otherwise you may need to set the new timeout. Also in the Java layer in the MessageQueue, there is a function nativeWake also can be called by JNI wake, call the nativeWake timing and call in Native wake time similar, insert a message in the message queue, there is also a case, the message queue head is a Barrier, and insert the message was the first message.
if(p==null||when==0||when<p.when){// New head, wake up the event queue if blocked.msg.next=p;mMessages=msg;needWake=mBlocked;}else{// Inserted within the middle of the queue. Usually we don't have to wake// up the event queue unless there is a barrier at the head of the queue// and the message is the earliest asynchronous message in the queue.needWake=mBlocked&&p.target==null&&msg.isAsynchronous();//If the head is Barrier and the new messages are asynchronous message “ &rdquo may need to wake up;Messageprev;for(;;){prev=p;p=p.next;if(p==null||when<p.when){break;}if(needWake&&p.isAsynchronous()){// The message queue is asynchronous message and the execution time in the news before, so there is no need to wake up. needWake=false;}}msg.next=p;// invariant: p == prev.nextprev.next=msg;}
In the head is inserted into the news does not have to call nativeWake, Because before may be performing IdleHandler, If the implementation of the IdleHandler, In the IdleHandler implementation of the nextPollTimeoutMillis is set to 0, Next time you start for cycle with the 0 call to nativePollOnce, Don’t need wake, Only when no message can perform (the message queue is empty or not to the execution time) and not set IdleHandlermBlocked to true.
If the Java layer of the message queue is Barrier Block and the insertion is an asynchronous message may need to wake up Looper, because the asynchronous message can be implemented in Barrier, but the asynchronous message must be the first time the implementation of asynchronous message.
Exit the Looper also need wake, removeSyncBarrier may also require.