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

本文详细解析了消息队列中的IdleHandler机制及其实现原理,包括如何检查并执行IdleHandler,以及如何通过epoll_wait进行事件监控。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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

// If first time, then get the number of idlers to run.

if(pendingIdleHandlerCount <0) {

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);

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

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

// Run the idle handlers.

// We only ever reach this code block during the first iteration.

for(inti =0; i 

finalIdleHandler idler = mPendingIdleHandlers[i];

mPendingIdleHandlers[i] = null;// release the reference to the handler

booleankeep =false;

try{

keep = idler.queueIdle();

} catch(Throwable t) {

Log.wtf("MessageQueue","IdleHandler threw exception", t);

}

if(!keep) {

synchronized(this) {

mIdleHandlers.remove(idler);

}

}

}

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

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

staticvoidandroid_os_MessageQueue_nativePollOnce(JNIEnv* env, jobject obj,

jint ptr, jint timeoutMillis) {

NativeMessageQueue* nativeMessageQueue = reinterpret_cast(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;

......

#ifdef LOOPER_USES_EPOLL

struct epoll_event eventItems[EPOLL_MAX_EVENTS];

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

bool acquiredLock = false;

#else

......

#endif

if (eventCount 

if (errno == EINTR) {

gotoDone;

}

LOGW("Poll failed with an unexpected error, errno=%d", errno);

result = ALOOPER_POLL_ERROR;

gotoDone;

}

if (eventCount == 0) {

......

result = ALOOPER_POLL_TIMEOUT;

gotoDone;

}

......

#ifdef LOOPER_USES_EPOLL

for(inti = 0; i 

intfd = eventItems[i].data.fd;

uint32_t epollEvents = eventItems[i].events;

if (fd == mWakeReadPipeFd) {

if (epollEvents & EPOLLIN) {

awoken();

} else{

LOGW("Ignoring unexpected epoll events 0x%x on wake read pipe.", 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 

intfd = eventItems[i].data.fd;

uint32_t epollEvents = eventItems[i].events;

if(fd == mWakeReadPipeFd) {

if(epollEvents & EPOLLIN) {

awoken();

} else{

LOGW("Ignoring unexpected epoll events 0x%x on wake read pipe.", epollEvents);

}

} else{

......

}

}

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

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

voidLooper::awoken() {

......

charbuffer[16];

ssize_t nRead;

do{

nRead = read(mWakeReadPipeFd, buffer, sizeof(buffer));

} while((nRead == -1 && errno == EINTR) || nRead ==sizeof(buffer));

}

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

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值