Handler native层实现原理

1 概述

​ 想要了解Handler 底层实现原理必须得了解以下知识,不然跳到底层去看源码也是一头雾水:

  1. C 与 C++,这个没什么好说的;

  2. Linux 的管道,主要是pipe函数 与 eventfd 函数来创建管道,pipe() , eventfd() ,主要了解这两个函数的作用就行,都是用于创建管道的函数;

  3. Linux 的epoll 机制;这个机制类似生产者和消费者模式,对文件的IO操作进行监听,当一个线程读取文件时,如果文件没有内容,这时线程就进入等待状态,另一个线程往文件写内容时,就会唤醒等待中的线程;

    本文基于Android8.0源码,几乎所有版本的Handler底层源码逻辑都一样,不同的是低版本使用pipe()函数来创建管道,而其他则使用eventfd() 函数来创建管道,其实都差不多,只是管道的创建方式不同而已,8.0源码使用的是eventfd函数;

​ 本文主要说的是Handler 底层实现,java层只会在捋逻辑的时候会带上一点。本文使用的native层文件有:

  1. android_os_MessageQueue.cpp ,位于/frameworks/base/core/jni/;
  2. Looper.cpp ,位于 /system/core/libutils/;

​ 涉及的类有NativeMessageQueue 与Looper,NativeMessageQueue 在 android_os_MessageQueue.cpp 文件中定义与实现,Looper 的实现就在Looper.cpp 中;

2 职责

  1. android_os_MessageQueue.cpp,相当于系统帮我们生成的native-lib.cpp 文件一样,文件中创建 NativeMessageQueue对象并返回给java层,本身不保存NativeMessageQueue;
  2. NativeMessageQueue,该类相当于一个代理对象,构造函数中创建了Looper对象并保存,最终调用到Looper 中去;
  3. Looper,所有的逻辑都在这个类中进行,非常重要;

3 Looper的初始化

  1. 首先java层的MessageQueue的构造函数调用了 nativeInit() , 最终调到android_os_MessageQueue.cpp文件 的 android_os_MessageQueue_nativeInit() 方法中,如下:

    MessageQueue(boolean quitAllowed) {mQuitAllowed = quitAllowed;mPtr = nativeInit();//这里mPrt得到的是c++层的 NativeMessageQueue
    }
    
    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对象,然后把它转成long类型返回给jave层的mPtr。android_os_MessageQueue.cpp本身不保存任何东西,所以把NativeMessageQueue对象交给jave层去保存。再看看NativeMessageQueue 构造函数做了什么:

    
    NativeMessageQueue::NativeMessageQueue() :mPollEnv(NULL), mPollObj(NULL), mExceptionObj(NULL) {mLooper = Looper::getForThread();if (mLooper == NULL) {mLooper = new Looper(false);Looper::setForThread(mLooper);}
    }
    

    首先调用Looper::getForThread() 来获取Looper对象,至于getForThread() 函数的实现这里不细说,作用相当java层的ThreadLocal 类一样,每个线程保存一个Looper对象,内部的实现关键其实是pthread_once()函数。第一次获取肯定为NULL,所以就new 一个Looper对象并保存起来;且通过Looper::setForThread(mLooper) 设置给当前线程;

  2. 进入的Looper的构造函数中

    Looper::Looper(bool allowNonCallbacks) :mAllowNonCallbacks(allowNonCallbacks), mSendingMessage(false),mPolling(false), mEpollFd(-1), mEpollRebuildRequired(false),mNextRequestSeq(0), mResponseIndex(0), mNextMessageUptime(LLONG_MAX) {mWakeEventFd = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC);LOG_ALWAYS_FATAL_IF(mWakeEventFd < 0, "Could not make wake event fd: %s",strerror(errno));//这段是打印AutoMutex _l(mLock);rebuildEpollLocked();
    }
    

    首先通过 eventfd()函数创建一个文件描述符,就是创建一个管道了,然后调用rebuildEpollLocked():(打印的代码去掉)

    void Looper::rebuildEpollLocked() {// 如果有旧的管道存在,则关闭if (mEpollFd >= 0) {#if DEBUG_CALLBACKSALOGD("%p ~ rebuildEpollLocked - rebuilding epoll set", this);
    #endifclose(mEpollFd);}// 开始根据epoll机制创建文件监听 mEpollFd = epoll_create(EPOLL_SIZE_HINT);struct epoll_event eventItem;memset(& eventItem, 0, sizeof(epoll_event)); // zero out unused members of data field unioneventItem.events = EPOLLIN;eventItem.data.fd = mWakeEventFd;//这个函数就是创建监听了int result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeEventFd, & eventItem);// 这个for循环也是一样注册监听,不过第一次进来mRequests 肯定没值,所有不走这里,低版本压根没这个forfor (size_t i = 0; i < mRequests.size(); i++) {const Request& request = mRequests.valueAt(i);struct epoll_event eventItem;request.initEventItem(&eventItem);int epollResult = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, request.fd, & eventItem);if (epollResult < 0) {ALOGE("Error adding epoll events for fd %d while rebuilding epoll set: %s",request.fd, strerror(errno));}}
    }
    

rebuildEpollLocked 主要功能就是通过 Linux的epoll机制来创建文件监听,不了解的epoll 的先去了解Linux 的epoll 机制;作用是监听mWakeEventFd 这个文件描述符的EPOLLIN 事件,既管道内有内容可读的时候,管道读取端会被唤醒,Looper的初始化到此结束。Looper初始化的工作有:

  1. 通过android_os_MessageQueue.cpp 文件创建NativeMessageQueue对象并返回给java层;
  2. NativeMessageQueue 构造函数创建Looper对象并保存起来;
  3. Looper构造函数内通过 eventfd() 函数创建一个管道,并通过epoll 机制对管道的EPOLLIN 事件进行监听;

4 消息的获取

都知道java层的消息获取都是在MessageQueue.java 类中的next() 方法,next() 方法通过nativePollOnce() 方法调到native层,如下:

 Message next() {final long ptr = mPtr;if (ptr == 0) {return null;}int pendingIdleHandlerCount = -1; int nextPollTimeoutMillis = 0; //这个变量的意义是,得到下个Message的时间for (;;) {if (nextPollTimeoutMillis != 0) {Binder.flushPendingCommands();}nativePollOnce(ptr, nextPollTimeoutMillis);synchronized (this) {final long now = SystemClock.uptimeMillis();Message prevMsg = null;Message msg = mMessages;if (msg != null && msg.target == null) {...}if (msg != null) {if (now < msg.when) {// 如果当前时间比最早的msg要小,就说明还要等待一段时间,所有nextPollTimeoutMillis 得到的值就是需要等待的时间nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);} else {//不需要等待了,得到一个Message...return msg;}} else {// 没有消息的情况下nextPollTimeoutMillis = -1;}。。。// 第一次进来,这个pendingIdleHandlerCount = -1,mMessages 也为NULLif (pendingIdleHandlerCount < 0 && (mMessages == null || now < mMessages.when)) {pendingIdleHandlerCount = mIdleHandlers.size();}if (pendingIdleHandlerCount <= 0) {//如果不需要额外处理,mBlocked 赋值为true后,直接 continue mBlocked = true;continue;}if (mPendingIdleHandlers == null) {mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];}mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);}// 处理一些回调for (int i = 0; i < pendingIdleHandlerCount; i++) {//...}pendingIdleHandlerCount = 0;nextPollTimeoutMillis = 0;}}

先看一下代码中的注释,第一次进入,首先调用nativePollOnce(ptr, nextPollTimeoutMillis); ptr 就是native层传过来的NativeMessageQueue, java层是如何计算nextPollTimeoutMillis 需要自己的捋流程了,nextPollTimeoutMillis 这个值是非常关键的,关系到线程的是否会进入等待状态。

进入nativePollOnce() 方法,调到android_os_MessageQueue.cpp 文件中的android_os_MessageQueue_nativePollOnce 函数中:

static void android_os_MessageQueue_nativePollOnce(JNIEnv* env, jobject obj,jlong ptr, jint timeoutMillis) {//拿到ptr转成NativeMessageQueue,再调到pollOnce()函数去NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr);nativeMessageQueue->pollOnce(env, obj, timeoutMillis);
}/**
* @paraem pollObj 是java 层的MessageQueue
*/
void NativeMessageQueue::pollOnce(JNIEnv* env, jobject pollObj, int timeoutMillis) {//做一下赋值操作,然后调到Looper的pollOnce() 函数中,注意timeoutMillis 此时的值为0 ;mPollEnv = env;mPollObj = pollObj;mLooper->pollOnce(timeoutMillis);mPollObj = NULL;mPollEnv = NULL;if (mExceptionObj) {//抛异常env->Throw(mExceptionObj);env->DeleteLocalRef(mExceptionObj);mExceptionObj = NULL;}
}

Looper:


int Looper::pollOnce(int timeoutMillis, int* outFd, int* outEvents, void** outData) {int result = 0;for (;;) {// 这个while 暂时不管,看样子是处理请求结果的,但是都还没请求哪来的结果while (mResponseIndex < mResponses.size()) {const Response& response = mResponses.itemAt(mResponseIndex++);int ident = response.request.ident;if (ident >= 0) {int fd = response.request.fd;int events = response.events;void* data = response.request.data;
#if DEBUG_POLL_AND_WAKEALOGD("%p ~ pollOnce - returning signalled identifier %d: ""fd=%d, events=0x%x, data=%p",this, ident, fd, events, data);
#endifif (outFd != NULL) *outFd = fd;if (outEvents != NULL) *outEvents = events;if (outData != NULL) *outData = data;return ident;}}//第一次进入result = 0,肯定不走这里,走下面的pollInner() 函数// 经过pollInner() 函数处理后,result的值赋值为一个小于0的值,然后走下面这个if,清除数据后直接返回// 这个返回直接就返回到java层了if (result != 0) {#if DEBUG_POLL_AND_WAKEALOGD("%p ~ pollOnce - returning result %d", this, result);
#endifif (outFd != NULL) *outFd = 0;if (outEvents != NULL) *outEvents = 0;if (outData != NULL) *outData = NULL;return result;}result = pollInner(timeoutMillis);}
}

pollOnce() 函数中开启了一个死循环,进入pollInner() 函数,注意此时的timeoutMillis 值为0,看看pollInner() 函数;


int Looper::pollInner(int timeoutMillis) {#if DEBUG_POLL_AND_WAKEALOGD("%p ~ pollOnce - waiting: timeoutMillis=%d", this, timeoutMillis);
#endif// timeoutMillis != 0 时,调整一下timeoutMillis的时间,timeoutMillis的值为0 则不走这里if (timeoutMillis != 0 && mNextMessageUptime != LLONG_MAX) {nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);int messageTimeoutMillis = toMillisecondTimeoutDelay(now, mNextMessageUptime);if (messageTimeoutMillis >= 0&& (timeoutMillis < 0 || messageTimeoutMillis < timeoutMillis)) {timeoutMillis = messageTimeoutMillis;}}// Poll.int result = POLL_WAKE;//POLL_WAKE的值为-1mResponses.clear();mResponseIndex = 0;mPolling = true;struct epoll_event eventItems[EPOLL_MAX_EVENTS];//该函数返回需要处理的事件数目,如返回0表示已超时。//这是个非常重要的函数,所以说需要先去了解epoll机制,//mEpollFd 就是epoll_create 创建的文件描述符//eventItems 处理的事件结果存放的地方//EPOLL_MAX_EVENTS  最大的处理数量//timeoutMillis 超时时间,0则是立即返回,-1 则是一直阻塞等待,等待被唤醒int eventCount = epoll_wait(mEpollFd, eventItems, EPOLL_MAX_EVENTS, timeoutMillis);mPolling = false;mLock.lock();// 是否重新初始化,里面调用了rebuildEpollLocked(),上面分析的Looper初始化Looper的构造函数也是调用rebuildEpollLocked()//一般情况不需要重新初始化if (mEpollRebuildRequired) {mEpollRebuildRequired = false;rebuildEpollLocked();goto Done;}//需要处理的事件数目小于0,返回if (eventCount < 0) {if (errno == EINTR) {goto Done;}result = POLL_ERROR;goto Done;}// 超时也直接返回if (eventCount == 0) {result = POLL_TIMEOUT;goto Done;}//如果有事件返回,就说明管道中有内容,则调用 awoken() 函数之后返回for (int i = 0; i < eventCount; i++) {int fd = eventItems[i].data.fd;uint32_t epollEvents = eventItems[i].events;if (fd == mWakeEventFd) {//mWakeEventFd 是Looper 构造函数中创建的文件描述符,就是管道if (epollEvents & EPOLLIN) {//判断事件是否是EPOLLIN事件awoken();} else {}} else {...}}
Done: ;//这里的代码很多,其实就是做一下清除操作...return result;
}
// result 的结果就是下面的四个枚举值
enum {POLL_WAKE = -1,POLL_CALLBACK = -2,POLL_TIMEOUT = -3,POLL_ERROR = -4,};

看代码的注释,注释说得比较清楚了,这个函数非常重要,函数内通过 epoll机制的epoll_wait() 函数来进行线程的阻塞和超时调起;

  1. 当epoll_wait() 返回,eventCount 小于等于0 说明没有事件,使用goto返回,返回的 result 的值都小于0
  2. 当epoll_wait() 返回,eventCount大于0则说明有事件,判断事件是否为EPOLLIN事件,是则调到 awoken() 函数

接下来看 awoken() 函数:

void Looper::awoken() {#if DEBUG_POLL_AND_WAKEALOGD("%p ~ awoken", this);
#endifuint64_t counter;TEMP_FAILURE_RETRY(read(mWakeEventFd, &counter, sizeof(uint64_t)));
}

使用了宏函数

#define TEMP_FAILURE_RETRY(exp) ({         \typeof (exp) _rc;                      \do {                                   \_rc = (exp);                       \} while (_rc == -1 && errno == EINTR); \_rc; })

不了解typeof 是什么的可以看一下这里 typeof, 最后一行是返回值,展开后大致如下:

int _rc;
do{_rc = read(mWakeEventFd, &counter, sizeof(uint64_t));
}while(_rc == -1 && errno == EINTR);
return _rc;// 这里的return 不是返回awoken() 函数,这里其实相当于awoken 里面调用了一个函数,而这个函数返回了_rc

作用是清空管道数据,保证每次处理的事件中,管道内的数据是最新的。至此,消息的获取、线程是如何阻塞与被唤醒的已经说完了,最关键的就是epoll 机制中的epoll_wait函数起到关键作用,所以了解epoll 机制是重中之重。

5 消息的发送

native 是没有消息的发送这种概念的,只有是否需要被唤醒,当没有消息的时候timeoutMillis = -1,epoll_wait() 函数则会处于阻塞状态,当java发送一条消息时,通过MessageQueue.java 的enqueueMessage() 方法把消息放到队列中去,然后判断是否需要唤醒线程,唤醒线程通过调用nativeWake() 方法, 调到android_os_MessageQueue.cpp文件的android_os_MessageQueue_nativeWake 函数:

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

通过java层传过来的ptr 转成NativeMessageQueue 对象再调用wake()函数:

void NativeMessageQueue::wake() {mLooper->wake();
}

又调到Looper 的wake() 函数中:

void Looper::wake() {#if DEBUG_POLL_AND_WAKEALOGD("%p ~ wake", this);
#endifuint64_t inc = 1;ssize_t nWrite = TEMP_FAILURE_RETRY(write(mWakeEventFd, &inc, sizeof(uint64_t)));if (nWrite != sizeof(uint64_t)) {if (errno != EAGAIN) {//判断,然后打印,不影响流程LOG_ALWAYS_FATAL("Could not write wake signal to fd %d: %s",mWakeEventFd, strerror(errno));}}
}

使用了宏函数:

#define TEMP_FAILURE_RETRY(exp) ({         \typeof (exp) _rc;                      \do {                                   \_rc = (exp);                       \} while (_rc == -1 && errno == EINTR); \_rc; })

转换后如下:

uint64_t inc = 1;
ssize_t nWrite = ({int _rc;do{_rc = write(mWakeEventFd, &inc, sizeof(uint64_t));}while(_rc == -1 && errno == EINTR);_rc; //把_rc 赋值给nWrite变量;
})

​ 唤醒就是往管道中写入内容,前面说了通过epoll_ctl() 函数对管道注册了监听,当有内容往管道写时,epoll_wait() 函数就会被唤醒,唤醒的流程就是这么简单。

​ Handler 的native层原理至此结束,这里就说了底层代码实现原理,还得配合java层逻辑一起看才能更加理解透彻Handler的流程及原理,至于java层我相信难度不大,结合文章再去阅读Handler的源码才会真正明白Handler的整个流程。

Handler native层实现原理相关推荐

  1. Android native层Hander原理分析

    目录 概述 源 自实现 下面简单叙述其原理: 如何使用: 注意问题: 概述 本篇探究 Android framework native层多媒体库中的 AHandler+ALooper+AMessage ...

  2. Android基础架构:Native层 Looper、Handler、Message 研究

    Android基础架构:Native层 Looper.Handler.Message 研究1,参考: https://www.cnblogs.com/roger-yu/p/15099541.html ...

  3. Native层HIDL服务的获取原理-Android10.0 HwBinder通信原理(七)

    摘要:本节主要来讲解Android10.0 Native层HIDL服务的获取原理 阅读本文大约需要花费23分钟. 文章首发微信公众号:IngresGe 专注于Android系统级源码分析,Androi ...

  4. Native层HIDL服务的注册原理-Android10.0 HwBinder通信原理(六)

    摘要:本节主要来讲解Android10.0 Native层HIDL服务的注册原理 阅读本文大约需要花费23分钟. 文章首发微信公众号:IngresGe 专注于Android系统级源码分析,Androi ...

  5. 【Android 异步操作】Handler 机制 ( MessageQueue 消息队列的阻塞机制 | Java 层机制 | native 层阻塞机制 | native 层解除阻塞机制 )

    文章目录 一.MessageQueue 的 Java 层机制 二.MessageQueue 的 native 层阻塞机制 三.MessageQueue 的 native 层解除阻塞机制 三.Messa ...

  6. Handler消息机制-Native层

    Handler消息机制-Native层 android12-release Handler消息机制-FWK层 1. MessageQueue连接Native层 1.1 nativeInit初始化 Na ...

  7. Android Framework学习(八)之Handler消息机制(Native层)解析

    在深入解析Android中Handler消息机制一文中,我们学习了Handler消息机制的java层代码,这次我们来学习Handler消息机制的native层代码. 在Java层的消息处理机制中,Me ...

  8. 【Binder 机制】Native 层 Binder 机制分析 ( binder_loop | svcmgr_handler | binder.c | binder_parse )

    文章目录 前言 一.binder_loop 方法调用 二.binder_loop 方法参数 svcmgr_handler 三.binder_loop 方法 四.binder_parse 方法 前言 在 ...

  9. Android逆向之旅---Native层的Hook神器Cydia Substrate使用详解

    一.前言 在之前已经介绍过了Android中一款hook神器Xposed,那个框架使用非常简单,方法也就那几个,其实最主要的是我们如何找到一个想要hook的应用的那个突破点.需要逆向分析app即可.不 ...

  10. Android --- IjkPlayer 阅读native层源码之解码成功后的音频数据如何发送回Android播放(九)

    整章目录:Android------- IjkPlayer 源码学习目录 本篇会有很多源代码,请注意阅读每行代码上面的注释. 本篇介绍的主要内容为上图红框圈起部分: 在前面介绍了如何将一个AvPack ...

最新文章

  1. Crawler:基于urllib库获取cn-proxy代理的IP地址
  2. 定价相关的主要用户出口
  3. AIX 系统中 PVID 的含义与作用
  4. 2440 nand flash和nor flash上启动
  5. 校门外的树+矩阵旋转
  6. stm32程序中的assert_param()的说明
  7. 根据条件动态修改element 组件深层次样式
  8. 编程随想 关系图_邹军:玩转数控编程,这里有个大招分享给你
  9. 齐聚一堂:共话网络安全人才培养新模式
  10. Service的绑定过程
  11. 系统安全性之认证技术
  12. 种植业适宜性评价算法
  13. 社群运营中品牌化和IP化运营实践
  14. 飞鸽短信平台发送国际短信
  15. 2020秋季甲级PAT 7-4 Professional Ability Test (30 分)
  16. h5将数字翻译为大写汉字_JS将数字转换为大写汉字人民币
  17. 很遗憾未能成功连接服务器神武,神武十年《见证》逍遥游戏里的超级学霸 最希望被别人抄“作业”...
  18. 中文命名实体识别mxnet_bertner_cn
  19. 少儿编程scratch如何快速上手?
  20. 郭鹤年--亚洲糖王与酒店巨子

热门文章

  1. Oracle创建临时表
  2. 《图书管理系统》-用例图、活动图与时序图简单绘制-startUML
  3. ios定位权限plist_[译] iOS 请求定位权限
  4. delphi random_delphi产生随机数
  5. C++ 单例模式(饿汉模式、懒汉模式)
  6. Hexo的Next主题详细配置
  7. SC-FDE 系统 基于导频的细小偏估计
  8. OA与EHR系统集成方案
  9. canoco5主成分分析步骤_主成分分析(PCA)统计与MATLAB函数实现
  10. 2021年高压电工模拟考试题及高压电工模拟考试题库