[Android] Handler源码解析 (Native层)

来源:互联网 发布:白话数据库三大范式 编辑:程序博客网 时间:2024/06/04 12:51

接前文[Android] Handler源码解析 (Java层),接下来对Handler机制在Native层上作解析。

Java层的MessageQueue中有4个native方法:

// 初始化和销毁private native static long nativeInit();private native static void nativeDestroy(long ptr);// 等待和唤醒private native static void nativePollOnce(long ptr, int timeoutMillis);private native static void nativeWake(long ptr);// 判断native层的状态private native static boolean nativeIsIdling(long ptr);

下面分别进行介绍。

nativeInit()和nativeDestroy(long ptr)

nativeInit()在MessageQueue初始化时被调用,返回一个long值,保存在mPtr中。

MessageQueue(boolean quitAllowed) {    mQuitAllowed = quitAllowed;    mPtr = nativeInit();}

nativeInit()的实现在/frameworks/base/core/jni/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);}

该JNI方法新建了一个NativeMessageQueue对象,然后将其指针用reinterpret_cast为long并返回给java层。同样地:

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

nativeDestory()方法中,将long型的ptr转换为NativeMessageQueue指针,然后再销毁对象。

NativeMessageQueue对象初始化的代码如下所示:

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

可以看到初始化方法中对mLooper进行了赋值。留意到Looper::getForThread();一句,结合其下的代码,猜想这是类似ThreadLocal模式的应用。接下来看看Looper类。

Looper类的声明在/system/core/include/utils/中,实现在/system/core/libutils/中,先来看一下Looper类的初始化方法:

Looper::Looper(bool allowNonCallbacks) :        mAllowNonCallbacks(allowNonCallbacks), mSendingMessage(false),        mResponseIndex(0), mNextMessageUptime(LLONG_MAX) {    int wakeFds[2];    // 1. 创建一个匿名管道,    //    wakeFds[0]代表管道的输出,应用程序读它。    //    wakeFds[1]代表管道的输入,应用程序写它。    int result = pipe(wakeFds);    LOG_ALWAYS_FATAL_IF(result != 0, "Could not create wake pipe.  errno=%d", errno);    mWakeReadPipeFd = wakeFds[0];    mWakeWritePipeFd = wakeFds[1];    // 2. 设置读写管道为non-blocking    result = fcntl(mWakeReadPipeFd, F_SETFL, O_NONBLOCK);    LOG_ALWAYS_FATAL_IF(result != 0, "Could not make wake read pipe non-blocking.  errno=%d",            errno);    result = fcntl(mWakeWritePipeFd, F_SETFL, O_NONBLOCK);    LOG_ALWAYS_FATAL_IF(result != 0, "Could not make wake write pipe non-blocking.  errno=%d",            errno);    mIdling = false;    // 3. 新建epoll实体,并将读管道注册到epoll    mEpollFd = epoll_create(EPOLL_SIZE_HINT);    LOG_ALWAYS_FATAL_IF(mEpollFd < 0, "Could not create epoll instance.  errno=%d", errno);    struct epoll_event eventItem;    memset(& eventItem, 0, sizeof(epoll_event)); // zero out unused members of data field union    // 表示对应的文件描述符可以读时触发event            eventItem.events = EPOLLIN;    eventItem.data.fd = mWakeReadPipeFd;    result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeReadPipeFd, & eventItem);    LOG_ALWAYS_FATAL_IF(result != 0, "Could not add wake read pipe to epoll instance.  errno=%d",            errno);}

从上面可以看出,Looper对象中维护着两个描述符,分别用于读和写。其中读描述符注册到epoll中。合理猜想looper的夸进程的睡眠和唤醒机制是通过epoll实现的。目标线程在读描述符mWakeReadPipeFd上等待,其他线程往mWakeWritePipeFd写入数据时,即可通过epoll机制将目标线程唤醒。

nativePollOnce(long ptr, int timeoutMillis)和nativeWake(long ptr)

nativePollOnce和nativeWake方法的实现如下所示:

void NativeMessageQueue::pollOnce(JNIEnv* env, int timeoutMillis) {    mInCallback = true;    mLooper->pollOnce(timeoutMillis);    mInCallback = false;    if (mExceptionObj) {        env->Throw(mExceptionObj);        env->DeleteLocalRef(mExceptionObj);        mExceptionObj = NULL;    }}void NativeMessageQueue::wake() {    mLooper->wake();}

可见这两个方法只是对Looper类的pollOnce和wake方法的简单封装。先看一下Looper对象的pollOnce方法实现如下所示:

inline int pollOnce(int timeoutMillis) {    return pollOnce(timeoutMillis, NULL, NULL, NULL);}...int Looper::pollOnce(int timeoutMillis, int* outFd, int* outEvents, void** outData) {    int result = 0;    for (;;) {        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 (outFd != NULL) *outFd = fd;                if (outEvents != NULL) *outEvents = events;                if (outData != NULL) *outData = data;                return ident;            }        }        if (result != 0) {            if (outFd != NULL) *outFd = 0;            if (outEvents != NULL) *outEvents = 0;            if (outData != NULL) *outData = NULL;            return result;        }        result = pollInner(timeoutMillis);    }}

先不管什么mResponses、outFd、outEvents和outData,我们先来看一下pollInner的实现。pollInner实现比较复杂,这里只看对本文有用的部分:

int Looper::pollInner(int timeoutMillis) {    ...    // 1. 设置默认result    int result = POLL_WAKE;    ...    // 2. 开始在mWakeReadPipeFd上等待    mIdling = true;    struct epoll_event eventItems[EPOLL_MAX_EVENTS];    int eventCount = epoll_wait(mEpollFd, eventItems, EPOLL_MAX_EVENTS, timeoutMillis);    // 3. 等待结束    mIdling = false;    ...    // 4. 根据epoll_wait返回的结果设置result    if (eventCount < 0) {        if (errno == EINTR) {            goto Done;        }        ALOGW("Poll failed with an unexpected error, errno=%d", errno);        result = POLL_ERROR;        goto Done;    }    // Check for poll timeout.    if (eventCount == 0) {        result = POLL_TIMEOUT;        goto Done;    }    // 5. 通过awoken()从mWakeReadPipeFd读出标记字符“W”    for (int i = 0; i < eventCount; i++) {        int fd = eventItems[i].data.fd;        uint32_t epollEvents = eventItems[i].events;        if (fd == mWakeReadPipeFd) {            if (epollEvents & EPOLLIN) {                awoken();            } else {                ALOGW("Ignoring unexpected epoll events 0x%x on wake read pipe.", epollEvents);            }        } else {            ...        }    }Done: ;    ...    return result;}

awoken()的实现代码如下所示:

void Looper::awoken() {    char buffer[16];    ssize_t nRead;    do {        nRead = read(mWakeReadPipeFd, buffer, sizeof(buffer));    } while ((nRead == -1 && errno == EINTR) || nRead == sizeof(buffer));}

awoken()只是简单地读出wake()在mWakeWritePipeFd上写入的数据。Looper对象的wake方法实现如下所示:

void Looper::wake() {    ssize_t nWrite;    do {        nWrite = write(mWakeWritePipeFd, "W", 1);    } while (nWrite == -1 && errno == EINTR);    if (nWrite != 1) {        if (errno != EAGAIN) {            ALOGW("Could not write wake signal, errno=%d", errno);        }    }}

正如前面所述,往mWakeWritePipeFd写数据即可唤醒在mWakeReadPipeFd上等待的线程。

总结

综上,在native层,一次wait/wake过程简述如下:

  1. native层Looper对象初始化时,新建了一个匿名管道,并将读管道(mWakeReadPipeFd)注册到epoll上。
  2. pollOnce方法调用pollInner方法,其中epoll_wait方法在mWakeReadPipeFd上等待读取。(wait
  3. wake方法被调用,往写管道(mWakeWritePipeFd)上写入字符“W”。
  4. pollInner方法继续执行,调用awoken从mWakeReadPipeFd读出数据。(wake

可画出框架图如下所示:

            +------------------+            |      Handler     |            +----^--------+----+                 |        |        dispatch |        | send                 |        |                 |        v                +----+ <---+                |          |                |  Looper  |                |          |                |          |                +---> +----+                  ^      |             next |      | enqueue                  |      |         +--------+------v----------+         |       MessageQueue       |         +--------+------+----------+                  |      |  nativePollOnce  |      |   nativeWake                  |      |+----------------------------------------------+ Native Layer                  |      |       pollOnce   |      |  wake                  |      |         +--------v------v--------+         |   NativeMessageQueue   |         +--------+------+--------+                  |      |         pollOnce |      |  wake         pollInner|      |  awoken                  |      |              +---v------v---+              |    Looper    |              +-+----------+-+                |          |     epoll_wait |          |  wake  +-------------v-+      +-v--------------+  |mWakeReadPipeFd|      |mWakeWritePipeFd|  +-------------^-+      +-+--------------+                |          |          read  |          | write                |          |              +-+----------v-+              |     Pipe     |              +--------------+

查看原文:http://legendmohe.net/2015/10/26/android-handler%e6%ba%90%e7%a0%81%e8%a7%a3%e6%9e%90-native%e5%b1%82/

0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 落地扇的机头摇摆的地方坏了怎么办 跌倒在楼梯上右侧肋骨骆上怎么办 1岁3个月害怕自己不敢走路怎么办 苹果手机没开定位丢了怎么办 我和我老婆每天都吵架怎么办 现在在学注册消防师好枯燥怎么办 店铺台阶太高顾客不愿进来怎么办? 上古卷轴5跑步要沉下去怎么办 1岁半宝宝半夜醒来不睡觉怎么办 上古卷轴5不小心偷了东西怎么办 47牙缺失17号长长了怎么办 碎纸机过热件亮了卡住纸了怎么办 汽车买贵了2万多怎么办 宝宝眼皮被蚊子咬肿了怎么办 一岁宝宝撞头咬到舌头有伤口怎么办 二胎快生了老大特别粘人怎么办 生二胎不舍得大宝跟奶奶睡怎么办 怀二胎婆婆不帮忙带孩子怎么办 注册过的高铁用户名忘了怎么办 硕士延期毕业找好的工作怎么办 竞彩足球绑定信用卡提不了现怎么办 qq启动出现问题请卸载重装怎么办 u盘有文件打开后却是空的怎么办 王者荣耀不记得所在的区服怎么办 交易猫出售游戏账号是微信号怎么办 网银密码输错3次怎么办 无线网卡信号很好就是没网速怎么办 红米2a忘了登陆账号怎么办 qq封了密保手机没用了怎么办 乐视手机重置账号密码忘了怎么办 此版本的ios不支持银联怎么办 单反m档拍出来照片是黑色怎么办 从兴趣部落老发骚扰信息怎么办 在厂里辞一个月厂长不批怎么办 在厂里做管理被员工恐吓怎么办 在葡京娱乐输了很多钱怎么办 从珠海入镜澳门北京往返签注怎么办 艾艾灸灸了一身小子子怎么办? 微信视频已过期或已清理怎么办 视频已过期或已被清理怎么办 小孩作业不会老婆天天吵骂打怎么办