1200字范文,内容丰富有趣,写作的好帮手!
1200字范文 > Android应用程序消息处理机制(Looper Handler)分析(3)

Android应用程序消息处理机制(Looper Handler)分析(3)

时间:2022-08-03 14:01:20

相关推荐

Android应用程序消息处理机制(Looper Handler)分析(3)

回到MessageQueue函数中,它接下来就是在进入等待状态前,看看有没有IdleHandler是需要执行的:

//Iffirsttime,thengetthenumberofidlerstorun.if(pendingIdleHandlerCount<0){pendingIdleHandlerCount=mIdleHandlers.size();}if(pendingIdleHandlerCount==0){//Noidlehandlerstorun.Loopandwaitsomemore.mBlocked=true;continue;}if(mPendingIdleHandlers==null){mPendingIdleHandlers=newIdleHandler[Math.max(pendingIdleHandlerCount,4)];}mPendingIdleHandlers=mIdleHandlers.toArray(mPendingIdleHandlers);

如果没有,即pendingIdleHandlerCount等于0,那下面的逻辑就不执行了,通过continue语句直接进入下一次循环,否则就要把注册在mIdleHandlers中的IdleHandler取出来,放在mPendingIdleHandlers数组中去。

接下来就是执行这些注册了的IdleHanlder了:

//Runtheidlehandlers.//Weonlyeverreachthiscodeblockduringthefirstiteration.for(inti=0;i<pendingIdleHandlerCount;i++){finalIdleHandleridler=mPendingIdleHandlers[i];mPendingIdleHandlers[i]=null;//releasethereferencetothehandlerbooleankeep=false;try{keep=idler.queueIdle();}catch(Throwablet){Log.wtf("MessageQueue","IdleHandlerthrewexception",t);}if(!keep){synchronized(this){mIdleHandlers.remove(idler);}}}

执行完这些IdleHandler之后,线程下次调用nativePollOnce函数时,就不设置超时时间了,因为,很有可能在执行IdleHandler的时候,已经有新的消息加入到消息队列中去了,因此,要重置nextPollTimeoutMillis的值:

//Whilecallinganidlehandler,anewmessagecouldhavebeendelivered//sogobackandlookagainforapendingmessagewithoutwaiting.nextPollTimeoutMillis=0;

分析完MessageQueue的这个next函数之后,我们就要深入分析一下JNI方法nativePollOnce了,看看它是如何进入等待状态的,这个函数定义在frameworks/base/core/jni/android_os_MessageQueue.cpp文件中:

staticvoidandroid_os_MessageQueue_nativePollOnce(JNIEnv*env,jobjectobj,jintptr,jinttimeoutMillis){NativeMessageQueue*nativeMessageQueue=reinterpret_cast<NativeMessageQueue*>(ptr);nativeMessageQueue->pollOnce(timeoutMillis);}

这个函数首先是通过传进入的参数ptr取回前面在Java层创建MessageQueue对象时在JNI层创建的NatvieMessageQueue对象,然后调用它的pollOnce函数:

voidNativeMessageQueue::pollOnce(inttimeoutMillis){mLooper->pollOnce(timeoutMillis);}

这里将操作转发给mLooper对象的pollOnce函数处理,这里的mLooper对象是在C++层的对象,它也是在前面在JNI层创建的NatvieMessageQueue对象时创建的,它的pollOnce函数定义在frameworks/base/libs/utils/Looper.cpp文件中:

intLooper::pollOnce(inttimeoutMillis,int*outFd,int*outEvents,void**outData){intresult=0;for(;;){......if(result!=0){......returnresult;}result=pollInner(timeoutMillis);}}

为了方便讨论,我们把这个函数的无关部分都去掉,它主要就是调用pollInner函数来进一步操作,如果pollInner返回值不等于0,这个函数就可以返回了。

函数pollInner的定义如下:

intLooper::pollInner(inttimeoutMillis){......intresult=ALOOPER_POLL_WAKE;......#ifdefLOOPER_USES_EPOLLstructepoll_eventeventItems[EPOLL_MAX_EVENTS];inteventCount=epoll_wait(mEpollFd,eventItems,EPOLL_MAX_EVENTS,timeoutMillis);boolacquiredLock=false;#else......#endifif(eventCount<0){if(errno==EINTR){gotoDone;}LOGW("Pollfailedwithanunexpectederror,errno=%d",errno);result=ALOOPER_POLL_ERROR;gotoDone;}if(eventCount==0){......result=ALOOPER_POLL_TIMEOUT;gotoDone;}......#ifdefLOOPER_USES_EPOLLfor(inti=0;i<eventCount;i++){intfd=eventItems[i].data.fd;uint32_tepollEvents=eventItems[i].events;if(fd==mWakeReadPipeFd){if(epollEvents&EPOLLIN){awoken();}else{LOGW("Ignoringunexpectedepollevents0x%xonwakereadpipe.",epollEvents);}}else{......}}if(acquiredLock){mLock.unlock();}Done:;#else......#endif......returnresult;}

这里,首先是调用epoll_wait函数来看看epoll专用文件描述符mEpollFd所监控的文件描述符是否有IO事件发生,它设置监控的超时时间为timeoutMillis:

inteventCount=epoll_wait(mEpollFd,eventItems,EPOLL_MAX_EVENTS,timeoutMillis);

回忆一下前面的Looper的构造函数,我们在里面设置了要监控mWakeReadPipeFd文件描述符的EPOLLIN事件。

当mEpollFd所监控的文件描述符发生了要监控的IO事件后或者监控时间超时后,线程就从epoll_wait返回了,否则线程就会在epoll_wait函数中进入睡眠状态了。返回后如果eventCount等于0,就说明是超时了:

if(eventCount==0){......result=ALOOPER_POLL_TIMEOUT;gotoDone;}

如果eventCount不等于0,就说明发生要监控的事件:

for(inti=0;i<eventCount;i++){intfd=eventItems[i].data.fd;uint32_tepollEvents=eventItems[i].events;if(fd==mWakeReadPipeFd){if(epollEvents&EPOLLIN){awoken();}else{LOGW("Ignoringunexpectedepollevents0x%xonwakereadpipe.",epollEvents);}}else{......}}

这里我们只关注mWakeReadPipeFd文件描述符上的事件,如果在mWakeReadPipeFd文件描述符上发生了EPOLLIN就说明应用程序中的消息队列里面有新的消息需要处理了,接下来它就会先调用awoken函数清空管道中把内容,以便下次再调用pollInner函数时,知道自从上次处理完消息队列中的消息后,有没有新的消息加进来。

函数awoken的实现很简单,它只是把管道中的内容都读取出来:

voidLooper::awoken(){......charbuffer[16];ssize_tnRead;do{nRead=read(mWakeReadPipeFd,buffer,sizeof(buffer));}while((nRead==-1&&errno==EINTR)||nRead==sizeof(buffer));}

因为当其它的线程向应用程序的消息队列加入新的消息时,会向这个管道写入新的内容来通知应用程序主线程有新的消息需要处理了,下面我们分析消息的发送的时候将会看到。

这样,消息的循环过程就分析完了,这部分逻辑还是比较复杂的,它利用Linux系统中的管道(pipe)进程间通信机制来实现消息的等待和处理,不过,了解了这部分内容之后,下面我们分析消息的发送和处理就简单多了。

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。