Handler native层实现原理

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_CALLBACKS
            ALOGD("%p ~ rebuildEpollLocked - rebuilding epoll set", this);
    #endif
            close(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 union
        eventItem.events = EPOLLIN;
        eventItem.data.fd = mWakeEventFd;
        //这个函数就是创建监听了
        int result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeEventFd, & eventItem);
       
    
        // 这个for循环也是一样注册监听,不过第一次进来mRequests 肯定没值,所有不走这里,低版本压根没这个for
        for (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 也为NULL
                if (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_WAKE
                ALOGD("%p ~ pollOnce - returning signalled identifier %d: "
                        "fd=%d, events=0x%x, data=%p",
                        this, ident, fd, events, data);
#endif
                if (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_WAKE
            ALOGD("%p ~ pollOnce - returning result %d", this, result);
#endif
            if (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_WAKE
    ALOGD("%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的值为-1
    mResponses.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_WAKE
    ALOGD("%p ~ awoken", this);
#endif

    uint64_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_WAKE
    ALOGD("%p ~ wake", this);
#endif

    uint64_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的整个流程。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值