写点什么

Android 基础进阶 - 消息机制 之 Native 层分析,统统给你解决

用户头像
Android架构
关注
发布于: 刚刚

needWake = mBlocked && p.target == null && msg.isAsynchronous();//具体实现如下,这个画张图来说明//链表引入一个 prev 变量,该变量指向 p 也 message(如果是 for 循环的内部第一次执行),然后把 p 进行向 next 移动,和需要插入的 Message 进行比较 whenMessage prev;


for (;;) {prev = p;p = p.next;if (p == null || when < p.when) {break;}if (needWake && p.isAsynchronous()) {needWake = false;}}msg.next = p;prev.next = msg;}


//如果插入的是异步消息,并且消息链表第一条消息是同步屏障消息。//或者消息链表中只有刚插入的这一个 Message,并且 mBlocked 为 true 即,正在阻塞状态,收到一个消息后也进入唤醒唤醒谁?MessageQueue.next 中被阻塞的 nativePollOnceif (needWake) {nativeWake(mPtr);}}return true;}


//android.os.MessageQueue#nextMessage next() {


//native 层 NativeMessageQueue 的指针 final long ptr = mPtr;if (ptr == 0) {return null;}


......for (;;) {


//阻塞操作,当等待 nextPollTimeoutMillis 时长,或者消息队列被唤醒//nativePollOnce 用于“等待”, 直到下一条消息可用为止. 如果在此调用期间花费的时间很长, 表明对应线程没有实际工作要做,或者 Native 层的 message 有耗时的操作在执行 nativePollOnce(ptr, nextPollTimeoutMillis);


}


可以看到在 MessageQueue#next 会调用课程阻塞的 native 方法nativePollOnce,在 MessageQueue#enqueueMessage 中如果需要唤醒会调用 native 方法nativeWake


问题是怎么阻塞的,怎么唤醒的,为什么要这样设计,直接在 Java 层完成处理不可以吗?


带着这样的困惑,开始 Native 层消息机制的分析学习。

2.1 MessageQueue Init 流程


图片来自:Android消息机制2-Handler(Native层)


调用 Native 方法初始化,返回值为 native 层的 NativeMessageQueue 指针地址


//android.os.MessageQueue#MessageQueue


MessageQueue(boolean quitAllowed) {mQuitAllowed = quitAllowed;//调用 Native 方法初始化,返回值为 native 层的 NativeMessageQueue 指针地址 mPtr = nativeInit();}


android_os_MessageQueue_nativeInit


//java native 方法 nativeInit 的 jni 方法,返回类型 long,即 mptrstatic jlong android_os_MessageQueue_nativeInit(JNIEnv* env, jclass clazz) {//NativeMessageQueue 是一个内部类 NativeMessageQueue* nativeMessageQueue = new NativeMessageQueue();


//返回为 NativeMessageQueue 指针地址......return reinterpret_cast<jlong>(nativeMessageQueue);}


NativeMessageQueue 构造方法


会进行 Native 层的 Looper 创建。Java 层创建 Looper 然后再创建 MessageQueue,而在 Native 层则刚刚相反,先创建 NativeMessageQueue 然后再创建 Looper。


// core/jni/android_os_MessageQueue.cpp


//NativeMessageQueue 构造方法//Java 层创建 Looper 然后再创建 MessageQueue,//而在 Native 层则刚刚相反,先创建 NativeMessageQueue 然后再创建 Looper


//MessageQueue 是在 Java 层与 Native 层有着紧密的联系,//但是 Native 层的 Looper 与 Java 层的 Looper 没有任何关系 NativeMessageQueue::NativeMessageQueue() :mPollEnv(NULL), mPollObj(NULL), mExceptionObj(NULL) {//Looper::getForThread()获取当前线程的 Looper,相当于 Java 层的 Looper.myLooper()mLooper = Looper::getForThread();if (mLooper == NULL) {//如果为空, new 一个 native 层的 LoopermLooper = new Looper(false);//相当于 java 层的 ThreadLocal.set() ?Looper::setForThread(mLooper);}}


Natvie 层的 Looper 的构造


MessageQueue 是在 Java 层与 Native 层有着紧密的联系,但是 Native 层的 Looper 与 Java 层的 Looper 没有任何关系


// libutils/Looper.cpp


Looper::Looper(bool allowNonCallbacks): mAllowNonCallbacks(allowNonCallbacks),mSendingMessage(false),mPolling(false),mEpollRebuildRequired(false),mNextRequestSeq(0),mResponseIndex(0),mNextMessageUptime(LLONG_MAX) {//eventfd 事件句柄,负责线程通信,替换了之前版本的 pipe 构造唤醒事件 fdmWakeEventFd.reset(eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC));LOG_ALWAYS_FATAL_IF(mWakeEventFd.get() < 0, "Could not make wake event fd: %s", strerror(errno));


AutoMutex _l(mLock);//进行 epoll 句柄的创建和初始化 rebuildEpollLocked();}


epoll 句柄的创建、添加唤醒事件句柄到 epoll


//libutils/Looper.cpp


void Looper::rebuildEpollLocked() {......


//epoll_create1 创建一个 epoll 句柄实例,并注册 wake 管道 mEpollFd.reset(epoll_create1(EPOLL_CLOEXEC));


......


//epoll 事件结构体 struct epoll_event eventItem;memset(& eventItem, 0, sizeof(epoll_event)); // zero out unused members of data field unioneventItem.events = EPOLLIN;eventItem.data.fd = mWakeEventFd.get();


int result = epoll_ctl(mEpollFd.get(), EPOLL_CTL_ADD, mWakeEventFd.get(), &eventItem);


for (size_t i = 0; i < mRequests.size(); i++) {const Request& request = mRequests.valueAt(i);struct epoll_event eventItem;request.initEventItem(&eventItem);


//将唤醒事件句柄(request.fd),添加到 epolled 句柄(mEpollFd.get()),为 epoll 添加一个唤醒机制 int epollResult = epoll_ctl(mEpollFd.get(), EPOLL_CTL_ADD, request.fd, &eventItem);


......}}


Native 层的 init 流程主要内容如下:


  1. NativeQueueMessage 和 Looper 的初始化。

  2. 构建了 epoll 句柄,向 epoll 中添加 epoll 事件注册

2.2 消息读取流程


图片来自:Android消息机制2-Handler(Native层)


nativePollOnce


//core/jni/android_os_MessageQueue.cpp


static void android_os_MessageQueue_nativePollOnce(JNIEnv* env, jobject obj,jlong ptr, jint timeoutMillis) {//将 Java 层传过来的 mPtr 转换为 nativeMessageQueueNativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr);//调用 nativeMessageQueue 的 pollOncenativeMessageQueue->pollOnce(env, obj, timeoutMillis);}


NativeMessageQueue :: pollOnce


//core/jni/android_os_MessageQueue.cppvoid NativeMessageQueue::pollOnce(JNIEnv* env, jobject pollObj, int timeoutMillis) {......//又调用了 Natvie 的 Looper 的 pollOncemLooper->pollOnce(timeoutMillis);......}


Native 层 Looper::pollOnce


//libutils/Looper.cpp


/**


  • timeoutMillis:超时时长

  • outFd:发生事件的文件描述符

  • outEvents:当前 outFd 上发生的事件,包含以下 4 类事件 EVENT_INPUT:可读 EVENT_OUTPUT:可写 EVENT_ERROR:错误 EVENT_HANGUP:中断


*outData:上下文数据


/int Looper::pollOnce(int timeoutMillis, int outFd, int outEvents, void** outData) {int result = 0;for (;;) {......//关键实现在 pollInner 中 result = pollInner(timeoutMillis);}}


Looper::pollInner


先会调用 epoll_wait 进入阻塞专题,唤醒的场景 向 epoll 中添加的 epollevent 等待事件发生或者超时触发 nativeWake()方法,会向 eventfd 写入字符,进行唤醒。


然后进性要处理的事件收集,然后在做处理。Natvie 的消息的处理顺序如下


  1. 处理 Native 的 Message,调用 Native 的 Handler 来处理该 Message

  2. 处理 Resposne 数组,POLL_CALLBACK 类型的事件


//libutils/Looper.cpp


int Looper::pollInner(int timeoutMillis) {


......


// Poll.int result = POLL_WAKE;mResponses.clear();mResponseIndex = 0;


// We are about to idle.mPolling = true;


struct epoll_event eventItems[EPOLL_MAX_EVENTS];


//等待事件发生或者超时触发 nativeWake()方法,会向 eventfd 写入字符 int eventCount = epoll_wait(mEpollFd.get(), eventItems, EPOLL_MAX_EVENTS, timeoutMillis);


// No longer idling.mPolling = false;


// Acquire lock.mLock.lock();


// Rebuild epoll set if needed.if (mEpollRebuildRequired) {mEpollRebuildRequired = false;rebuildEpollLocked();goto Done;}


// Check for poll error.if (eventCount < 0) {if (errno == EINTR) {goto Done;}an unexpected error: %s", strerror(errno));result = POLL_ERROR;goto Done;}


for (int i = 0; i < eventCount; i++) {int fd = eventItems[i].data.fd;uint32_t epollEvents = eventItems[i].events;if (fd == mWakeEventFd.get()) {//已经唤醒,如果是 EPOLLIN 类型事件,读取并清空 eventfd 中的数据 if (epollEvents & EPOLLIN) {


awoken();}} else {ssize_t requestIndex = mRequests.indexOfKey(fd);if (requestIndex >= 0) {int events = 0;if (epollEvents & EPOLLIN) events |= EVENT_INPUT;if (epollEvents & EPOLLOUT) events |= EVENT_OUTPUT;if (epollEvents & EPOLLERR) events |= EVENT_ERROR;


《Android学习笔记总结+最新移动架构视频+大厂安卓面试真题+项目实战源码讲义》
浏览器打开:qq.cn.hn/FTe 免费领取
复制代码


if (epollEvents & EPOLLHUP) events |= EVENT_HANGUP;pushResponse(events, mRequests.valueAt(requestIndex));}}}


//上面是收集,Done 中是处理的部分,很多设计都是采用这种设计,逻辑分离


Done: ; //1. 先处理 Native 的 Message,调用 Native 的 Handler 来处理该 Message


mNextMessageUptime = LLONG_MAX;while (mMessageEnvelopes.size() != 0) {......if (messageEnvelope.uptime <= now) {{......handler->handleMessage(message);}} else {mNextMessageUptime = messageEnvelope.uptime;break;}}


//2. 再处理 Resposne 数组,POLL_CALLBACK 类型的事件,比如一些 // Invoke all response callbacks. for (size_t i = 0; i < mResponses.size(); i++) { Response& response = mResponses.editItemAt(i); if (response.request.ident == POLL_CALLBACK) { int fd = response.request.fd; int events = response.events; void* data = response.request.data; int callbackResult = response.request.callback->handleEvent(fd, events, data); } } return result; }


消息获取流程主要如下


  1. 依此调用 NativeMessageQueue 和 looper 中的 pollonce,最终调用到 Looper 的 pollInner

  2. pollInner 会调用 epoll_wait 进入阻塞,唤醒的条件是 epoll 中添加的事件响应了或者发生了超时等。_

  3. 先执行 Native 层的 Message,再执行 Native 层的 Resposne 数组,POLL_CALLBACK 类型的事件(比如一些按键事件等)

  4. 返回到 Java 层后再调用 Java 层 MessageQueue 中的读取 Message。

2.3 消息发送流程


图片来自:Android消息机制2-Handler(Native层)

用户头像

Android架构

关注

还未添加个人签名 2021.10.31 加入

还未添加个人简介

评论

发布
暂无评论
Android基础进阶 - 消息机制 之Native层分析,统统给你解决