您的位置:首页 > 汽车 > 新车 > 【18】Android 线程间通信(三) - Handler

【18】Android 线程间通信(三) - Handler

2024/10/5 20:26:14 来源:https://blog.csdn.net/mr_zengkun/article/details/140506784  浏览:    关键词:【18】Android 线程间通信(三) - Handler

概述

接下来我们会从native层来分析一下,Handler做了什么,以及之前提到过的应用层的两个native的调用链。

nativeWake

最早接触这个方法还记得是什么时候吗?MessageQueue#enqueueMessage中,在这个方法的末尾,我们看到了它的身影,通过判断条件needWake是否为真,从而执行到这个方法中。

	//MessageQueue.javamsg.markInUse();msg.when = when;Message p = mMessages;boolean needWake;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();Message prev;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;}// We can assume mPtr != 0 because mQuitting is false.if (needWake) {nativeWake(mPtr);}

我们首先看看这个needWake的条件是什么,从上面源码可以看到,这个needWake是一个局部变量,在方法内定义的。当进入第一个if条件判断,会给needWake赋值mBlocked。mMessage是保存的头节点的Message消息,当头结点消息为空(没消息队列暂时没有消息),或者新消息延迟为0,又或新消息的延迟小于头节点消息的延迟,就进入条件体内部。而这个mBlocked在next方法中,会被赋值,当nativePollOnce未被阻塞,即当前有消息需要立即执行,mBlocked就为false。而当消息队列消息为空,IdleHandlers集合也为空,就会重新赋值给true。

进入else当中,也会给needWake赋值,满足mBlocked为true,且为头部为屏障消息,且新消息为异步消息,才会是true。

接着我们看一下这个mPtr,这个在MessageQueue中定义并且注解说明是专门为nativa code使用的。赋值是在MessageQueue创建的时候,通过nativeInit返回上来。这个先保留着,之后我们再慢慢分析。

	MessageQueue.java@UnsupportedAppUsage@SuppressWarnings("unused")private long mPtr; // used by native code

而nativeWake对应的native的类 /frameworks/base/core/jni/android_os_MessageQueue.cpp

//android_os_MessageQueue.cpp
void NativeMessageQueue::wake() {mLooper->wake();
}static void android_os_MessageQueue_nativeWake(JNIEnv* env, jclass clazz, jlong ptr) {NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr);nativeMessageQueue->wake();
}

进而调用到了native层的Looper中,继续看看Looper做了什么。/system/core/libutils/Looper.cpp

//Looper.cpp
void Looper::wake() {
#if DEBUG_POLL_AND_WAKEALOGD("%p ~ wake", this);
#endifuint64_t inc = 1;ssize_t nWrite = TEMP_FAILURE_RETRY(write(mWakeEventFd.get(), &inc, sizeof(uint64_t)));if (nWrite != sizeof(uint64_t)) {if (errno != EAGAIN) {LOG_ALWAYS_FATAL("Could not write wake signal to fd %d (returned %zd): %s",mWakeEventFd.get(), nWrite, strerror(errno));}}
}

这段代码执行到这里就执行完毕了。这段代码主要做了一个事情,就是通过write方法,向mWakeEventFd写入inc值。

fd 文件描述符。系统管理了一个fd列表,记录了各种不同的fd,mWakeEventFd是其中一个用于唤醒事件的fd。

这里我们一直跟踪下来,发现nativeWake走到native层,最后也只是在fd上写了一个值。而主要执行进来的手段,也是needWake判断条件为真,即:
1、消息队列没有消息的前提下,新消息的延迟when,小于头部消息。
2、消息队列没有消息的前提下,头部有一个同步屏障,且进来的新消息是一个异步消息。

nativePollOnce

这个方法接触的时候是在MessageQueue#next方法中,我们在文章一有补充说明到,nativePollOnce是一个阻塞方法,特定条件下会阻塞当前线程。

@UnsupportedAppUsageMessage next() {// Return here if the message loop has already quit and been disposed.// This can happen if the application tries to restart a looper after quit// which is not supported.final long ptr = mPtr;if (ptr == 0) {return null;}int pendingIdleHandlerCount = -1; // -1 only during first iterationint nextPollTimeoutMillis = 0;for (;;) {if (nextPollTimeoutMillis != 0) {Binder.flushPendingCommands();}nativePollOnce(ptr, nextPollTimeoutMillis);...}

next方法刚进入的时候,会初始化nextPollTimeoutMillis值为0,然后进入for循环,进入了nativePollOne,同nativeWake一样,会传入ptr这个值进去,同时把nextPollTimeoutMillis传入。

//android_os_MessageQueue.cpp
static void android_os_MessageQueue_nativePollOnce(JNIEnv* env, jobject obj,jlong ptr, jint timeoutMillis) {NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr);nativeMessageQueue->pollOnce(env, obj, timeoutMillis);
}void NativeMessageQueue::pollOnce(JNIEnv* env, jobject pollObj, int timeoutMillis) {...mLooper->pollOnce(timeoutMillis);...
}//Looper.cpp
int Looper::pollOnce(int timeoutMillis, int* outFd, int* outEvents, void** outData) {int result = 0;for (;;) {...result = pollInner(timeoutMillis);}
}int Looper::pollInner(int timeoutMillis) {...struct epoll_event eventItems[EPOLL_MAX_EVENTS];int eventCount = epoll_wait(mEpollFd.get(), eventItems, EPOLL_MAX_EVENTS, timeoutMillis);...for (int i = 0; i < eventCount; i++) {const SequenceNumber seq = eventItems[i].data.u64;uint32_t epollEvents = eventItems[i].events;if (seq == WAKE_EVENT_FD_SEQ) {if (epollEvents & EPOLLIN) {awoken();} else {ALOGW("Ignoring unexpected epoll events 0x%x on wake event fd.", epollEvents);}} else {const auto& request_it = mRequests.find(seq);if (request_it != mRequests.end()) {const auto& request = request_it->second;int events = 0;if (epollEvents & EPOLLIN) events |= EVENT_INPUT;if (epollEvents & EPOLLOUT) events |= EVENT_OUTPUT;if (epollEvents & EPOLLERR) events |= EVENT_ERROR;if (epollEvents & EPOLLHUP) events |= EVENT_HANGUP;mResponses.push({.seq = seq, .events = events, .request = request});} else {ALOGW("Ignoring unexpected epoll events 0x%x for sequence number %" PRIu64" that is no longer registered.",epollEvents, seq);}}...}...
}

这里直接贴出了整个调用链,前面的调用我们都不关心,因为最终还是把timeoutMillis传到了Looper#pollOnceInner中,省略了很多细节,只保留了我们关注的东西。epoll_wait函数,等待文件描述符(fd)上的事件,eventItems存储了发生的事件,timeoutMills应用传下来的超时时间。
1、timeoutMillis 为 -1,epoll_wait 将无限期地等待事件
2、 timeoutMillis 为 0,epoll_wait 将立即返回,不会阻塞
3、一个整数,超时时间,时间到了,epoll_wait也会返回

然后通过for循环取出eventItems中,上一个事件发生后到此之间所有发生的事件,并且判断是否属于WAKE_EVENT_FD_SEQ,这个fd有点眼熟吧,就是nativeWake中写入的那个fd。然后执行awoken进行唤醒。

epoll Linux底层提供的一个消息监听的接口
1、通过写入fd事件,使得epoll_wait将被唤醒。
2、timeoutMillis时间达到

至此,nativePollOnce我们就分析清楚了,当有事件发生,或者延时事件到达,就会取出期间所有发生的事件,判断事件类型是否属于wake事件,并唤醒应用层的nativePollOnce,让逻辑继续执行。

Ptr

这个东西不知道看到这里是否还记得它是什么?就是上面我们提到的在调用这两个native方法都会传入的数据类型,我们到现在还没有搞懂这个东西是做什么用的。只记得在MessageQueue初始化的过程会通过nativeInit返回这个值上来,并且后续将这个值传递下去。

//android_os_MessageQueue.cpp
static jlong android_os_MessageQueue_nativeInit(JNIEnv* env, jclass clazz) {NativeMessageQueue* nativeMessageQueue = new NativeMessageQueue();if (!nativeMessageQueue) {jniThrowRuntimeException(env, "Unable to allocate native queue");return 0;}nativeMessageQueue->incStrong(env);return reinterpret_cast<jlong>(nativeMessageQueue);
}

这里的nativeMessageQueue是一个指针,将指针类型转换为一个长整数类型返回回去。我们知道一个MessageQueue对应一个Looper,即对应一个线程。只有在创建MessageQueue的时候,会执行初始化,native层也会维护一个nativeMassageQueue的对象和应用层的MessageQueue一一对应。这样上层触发唤醒,或者阻塞的时候,下层native可以通过这个ptr拿到之前创建的NativeMessageQueue进行wake或者pollonce的操作了。

总结

1、nativePollOnce会阻塞线程
2、MessageQueue和NativeMessageQueue一一对应
3、nativeWake会写fd触发epoll_wait的监听
4、只有fd属于wake的类型才会唤醒线程
5、nativeWake唤醒的条件比较苛刻(从这里看)

这样一来,Handler的所有内容就基本讲完了,可能还涉及一些其他的内容大家可以自行补充一下,这里就不在多提了。这次Handler的相关内容整理下来,我自己的收获也是蛮多的。

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com