Handler机制(Native层)

在Android消息处理机制中,不仅包括了Java层的消息机制处理,还包括了Native消息处理机制(也拥有Handler、Looper、MessageQueue)
image
Java层的MessageQueue中声明了几个本地方法:

    private native static long nativeInit();
    private native static void nativeDestroy(long ptr);
    private native void nativePollOnce(long ptr, int timeoutMillis); /*non-static for callbacks*/
    private native static void nativeWake(long ptr);
    private native static boolean nativeIsPolling(long ptr);
    private native static void nativeSetFileDescriptorEvents(long ptr, int fd, int events);

Java层中MessageQueue在初始化时,会调用本地方法去创建Native MessageQueue,并通过mPtr保存了Native中MessageQueue的地址。对于Java层来说,Native层的NativeMessageQueue只用来处理线程的睡眠与唤醒,Java层发送的消息还是在Java层被处理。

MessageQueue(boolean quitAllowed) {
    mQuitAllowed = quitAllowed;
    mPtr = nativeInit(); // mPtr保存了Native中MessageQueue的地址
}

nativeInit()

初始化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()

Native Looper调用静态方法getForThread(),获取当前线程中的Looper对象。如果为空,则创建Native Looper对象。它和Java层的Looper无任何关系。Looper从ALooper派生

NativeMessageQueue::NativeMessageQueue() :
        mPollEnv(NULL), mPollObj(NULL), mExceptionObj(NULL) {
    // 取得保存在线程本地存储空间(Thread Local Storage)中的Looper对象
    mLooper = Looper::getForThread();
    if (mLooper == NULL) {
        mLooper = new Looper(false);
        // 如果looper已经存在就替换,如果为空就删除之前的
        Looper::setForThread(mLooper);
    }
}

// Looper::getForThread()
/**
 * Returns the looper associated with the calling thread, or NULL if
 * there is not one.
 */
sp<Looper> Looper::getForThread() {
    int result = pthread_once(& gTLSOnce, initTLSKey);
    LOG_ALWAYS_FATAL_IF(result != 0, "pthread_once failed");
    // 类似于Java层的ThreadLocal中的get()方法
    return (Looper*)pthread_getspecific(gTLSKey);
}

Looper()

mWakeEventFd:用于事件通知的文件描述符,在这里表示唤醒Looper的文件描述符。实际上它指向一个eventfd对象,该对象可以实现事件的等待和通知机制。

Looper::Looper(bool allowNonCallbacks) :
        mAllowNonCallbacks(allowNonCallbacks), mSendingMessage(false),
        mPolling(false), mEpollFd(-1), mEpollRebuildRequired(false),
        mNextRequestSeq(0), mResponseIndex(0), mNextMessageUptime(LLONG_MAX) {
     /**  这才是Linux后来才有的东西,负责线程通信,替换了老版本的pipe */
    // 创建一个用于唤醒Looper的文件描述符
    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);
    // 重建一个epoll
    rebuildEpollLocked();  
}

rebuildEpollLocked()

void Looper::rebuildEpollLocked() {
    // 如果已经有一个epoll了,那么关闭它
    if (mEpollFd >= 0) {
#if DEBUG_CALLBACKS
        ALOGD("%p ~ rebuildEpollLocked - rebuilding epoll set", this);
#endif
        close(mEpollFd);
    }

    // 通过Linux系统调用 创建一个epoll实例
    mEpollFd = epoll_create(EPOLL_SIZE_HINT);
    LOG_ALWAYS_FATAL_IF(mEpollFd < 0, "Could not create epoll instance: %s", strerror(errno));

    struct epoll_event eventItem;      //epoll_event 封装了fd、event等
    memset(& eventItem, 0, sizeof(epoll_event)); // 初始化为0
    eventItem.events = EPOLLIN;        //event:EPOLLIN 表示读事件,即对应的连接可读(缓冲区有值)
    eventItem.data.fd = mWakeEventFd; // 设置fd

    // epoll_ctl 系统调用,这里epoll添加了对mWakeEventFd的监听,
    // 当epoll监听到mWakeEventFd的eventItem出现时,就会通知Looper
    int result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeEventFd, & eventItem);
    LOG_ALWAYS_FATAL_IF(result != 0, "Could not add wake event fd to epoll instance: %s",
                        strerror(errno));

    // 对mRequest内的所有fd重新添加epoll监听(这里主要添加的是Input事件,如键盘、传感器输入)
    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));
        }
    }
}

通过上述代码我们知道:

  • Java层Looper的创建导致MessageQueue的创建,而Native层正好相反,NativeMessageQueue的创建导致了Looper的创建
  • MessageQueue是在Java层与Native层有着紧密的联系,但是此次Native层的Looper与Java层的Looper没有任何关系
  • Native层的Looper创建和Java层的也完全不一样,它利用了Linux的epoll机制监测了Input的fd和唤醒fd。从功能上来讲,这个唤醒fd才是真正处理Java Message和Native Message的钥匙。

管道

是Linux中进行进程通信或者线程通信的一种手段之一,管道分为匿名管道(pipe)(父子进程)以及命名管道(FIFO),管道是内核维护的一个缓存(内核缓冲区),在管道的两端,分别是两个打开文件文件描述符(fd),这两个打开文件描述符都是对应同一个文件,其中一个是用来读的,别一个是用来写的。它是半双工的。我们这里用的是匿名管道。

一般的使用方式就是,一个线程通过读文件描述符中来读管道的内容,当管道没有内容时,这个线程就会进入等待状态,而另外一个线程通过写文件描述符来向管道中写入内容,写入内容的时候,如果另一端正有线程正在等待管道中的内容,那么这个线程就会被唤醒。这个等待和唤醒的操作是如何进行的呢,这就要借助Linux系统中的epoll机制了。

如果两个进程同时进行读或者同时进行写,必会导致数据冲突,所以内核会对管道上锁(pipe_inode_info里的mutex),所以是半双工的(数据只能在一个方向上流动)指定读写端。当一个进程正在对pipe执行读写操作时,另一进程必须等待。

epoll机制

提供了Linux平台上最高效的I/O复用机制,epoll用法和select/poll非常类似,主要作用就是I/O复用,即在一个地方等待多个文件句柄的I/O事件。epoll机制为处理大批量句柄而作了改进的poll,是Linux下多路复用IO接口select/poll的增强版本,它能显著减少程序在大量并发连接中只有少量活跃的情况下的系统CPU利用率。
epoll机制详解(一)
epoll机制详解(二)

nativeWake()

在MessageQueue的enqueueMessage()方法中调用(needWake为true的时候),它会触发nativePollOnce()结束等待。

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

NativeMessageQueue # wake()

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

Looper # wake()

void Looper::wake() {
    uint64_t inc = 1;
    // 向管道mWakeEventFd(写端)写入字符1,此时epoll会侦测到这个写入事件,然后通知Looper,最后会把epoll_wait唤醒
    // TEMP_FAILURE_RETRY 是一个宏定义, 当执行write失败后,会不断重复执行,直到执行成功为止
    ssize_t nWrite = TEMP_FAILURE_RETRY(write(mWakeEventFd, &inc, sizeof(uint64_t)));
    if (nWrite != sizeof(uint64_t)) {
        if (errno != EAGAIN) {
            ALOGW("Could not write wake signal, errno=%d", errno);
        }
    }
}

nativePollOnce()

是在MessageQueue的next()方法中调用,读取事件

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

NativeMessageQueue # pollOnce()

void NativeMessageQueue::pollOnce(JNIEnv* env, jobject pollObj, int timeoutMillis) {
    mPollEnv = env;
    mPollObj = pollObj;
    // 
    mLooper->pollOnce(timeoutMillis);
    mPollObj = NULL;
    mPollEnv = NULL;

    if (mExceptionObj) {
        env->Throw(mExceptionObj);
        env->DeleteLocalRef(mExceptionObj);
        mExceptionObj = NULL;
    }
}

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;
    // 对fd对应的Responses进行处理,后面发现Response里都是活动fd
    for (;;) {
        // **先处理没有Callback的Response事件**
        while (mResponseIndex < mResponses.size()) {
            const Response& response = mResponses.itemAt(mResponseIndex++);
            int ident = response.request.ident; // ident是这个response的id
            if (ident >= 0) {
                // ident>=0则表示没有callback,因为POLL_CALLBACK=-2
                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;
                // 对于没有callback的response,pollOnce只是返回它的ident,并没有实际做什么处理
                // 因为没有callback,系统也不知道如何处理
                return ident;
            }
        }
        // 注意这里处于循环内部,改变result的值在后面的pollInner
        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);
    }
}

参数说明:

  • timeoutMillis:超时等待时间,如果值为-1,则表示无限等待,直到有事件发生为止。如果值为0,则无需等待立即返回
  • outFd:发生事件的文件描述符
  • outEvents:当前outFd上发生的事件,包含以下四类:
  • EVENT_INPUT 可读
  • EVENT_OUTPUT 可写
  • EVENT_ERROR 错误
  • EVENT_HANGUP 中断
  • outData:上下文数据,是由用户在添加监听句柄时传递的

pollOnce函数返回值:

  • ALOOPER_POLL_WAKE:表示这次返回是由wake函数触发的,即pipe写端的write事件触发
  • ALOOPER_POLL_TIMEOUT:表示等待超时
  • ALOOPER_POLL_ERROR: 表示等待过程中发生错误
  • ALOOPER_POLL_CALLBACK:表示某个被监听的fd因某种原因被触发(outFd:发生事件的文件句柄;outEvents:当前outFd上发生的事件)

Looper # pollInner()

int Looper::pollInner(int timeoutMillis) {
    ...
    int result = POLL_WAKE;
    mResponses.clear();
    mResponseIndex = 0;
    mPolling = true; //即将处于idle状态
    struct epoll_event eventItems[EPOLL_MAX_EVENTS]; //fd最大个数为16
    
    //等待事件发生或者超时,在nativeWake()方法,向管道写端写入字符,则该方法会返回;
    // 进行系统调用,阻塞式等待,收集在epoll监控的事件中已经发生的事件,超过timeoutMillis则返回
    // 实际上这里epoll监听所有已添加的fd的改变,比如写入了“1”,那么就能收集到该事件
    int eventCount = epoll_wait(mEpollFd, eventItems, EPOLL_MAX_EVENTS, timeoutMillis);

    mPolling = false; //不再处于idle状态
    mLock.lock();  //请求锁
    if (mEpollRebuildRequired) {
        mEpollRebuildRequired = false;
        rebuildEpollLocked();  // epoll重建,直接跳转Done;
        goto Done;
    }
    if (eventCount < 0) {
        if (errno == EINTR) {
            goto Done;
        }
        result = POLL_ERROR; // epoll事件个数小于0,发生错误,直接跳转Done;
        goto Done;
    }
    if (eventCount == 0) {  //epoll事件个数等于0,发生超时,直接跳转Done;
        result = POLL_TIMEOUT;
        goto Done;
    }

    //循环遍历,处理所有的事件
    for (int i = 0; i < eventCount; i++) {
        int fd = eventItems[i].data.fd;
        uint32_t epollEvents = eventItems[i].events;
        /*
        之前通过pipe函数创建过两个fd,这里根据fd知道是管道读端有可读事件。
        读者还记得对nativeWake函数的分析吗?在那里我们向管道写端写了一个”W”字符,这样
        就能触发管道读端从epoll_wait函数返回了
        */
        if (fd == mWakeEventFd) {
            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;
                if (epollEvents & EPOLLHUP) events |= EVENT_HANGUP;
                //处理request,生成对应的reponse对象,push到响应数组
                pushResponse(events, mRequests.valueAt(requestIndex));
            }
        }
    }
Done: ;
    //再处理Native的Message,调用相应回调方法
    mNextMessageUptime = LLONG_MAX;
    while (mMessageEnvelopes.size() != 0) {
        nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
        const MessageEnvelope& messageEnvelope = mMessageEnvelopes.itemAt(0);
        if (messageEnvelope.uptime <= now) {
            {
                sp<MessageHandler> handler = messageEnvelope.handler;
                Message message = messageEnvelope.message;
                mMessageEnvelopes.removeAt(0);
                mSendingMessage = true;
                mLock.unlock();  //释放锁
                handler->handleMessage(message);  // 调用Native的handler处理消息事件
            }
            mLock.lock();  //请求锁
            mSendingMessage = false;
            result = POLL_CALLBACK; // 发生回调
        } else {
            mNextMessageUptime = messageEnvelope.uptime;
            break;
        }
    }
    mLock.unlock(); //释放锁

    // 前面先收集request,等native message消息处理完之后再做处理(native message优先级高于监控fd的优先级)
    //处理带有Callback()方法的Response事件,执行Reponse相应的回调方法
    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);
            // 如果为0表明不需要再次监视该文件句柄
            if (callbackResult == 0) {
                removeFd(fd, response.request.seq); //移除fd
            }
            response.request.callback.clear(); //清除reponse引用的回调方法
            result = POLL_CALLBACK;  // 发生回调
        }
    }
    return result;
}

pollInner函数的流程:

  • 1)首先计算-~下真正需要等待的时间

  • 2)调用epoll wait 函数等待

    1. epoll_ wait 函数返回,这时候可能有3种情况:
    • 发生错误,则跳转到Done处。
    • 超时,也跳转到Done处。
    • epoll_ wait 监测到某些文件句柄上有事件发生。
  • 4)假设epoll_ wait 因为文件句柄有事件而返回,此时需要根据文件句柄来分别处理:

    • 如果是管道读端发生事件,则认为是控制命令,可以直接读取管道中的数据。
    • 如果是其他fd发生事件,则根据Request构造Response,并push到Response数组中。
  • 5)真正开始处理事件是在有Done标志的位置

    • 首先处理Native的Message。调用Native Handler的handleMessage处理该Message。
    • 处理Response数组中那些带有callback的事件。

先是处理Native的Message,然后处理Native的Request,最后才是处理Java的Message(调用Looper#wake()以唤醒Looper。当native的消息处理完成后,会导致Java层的nativePollOnce()的调用返回,从而让Java层处理消息)

Looper # awoken()

void Looper::awoken() {
    uint64_t counter;
    // 不断读取管道数据,目的就是为了清空管道内容
    TEMP_FAILURE_RETRY(read(mWakeEventFd, &counter, sizeof(uint64_t)));
}

总结

NativeMessageQueue

在Java层的MessageQueue的构造函数中,调用了nativeInit创建了NativeMessageQueue对象,并且把指针变量返回给Java层的mPtr。在NativeMessageQueue的构造函数中,会在当前线程中创建C++的Looper对象

Looper

控制eventfd的读写,通过epoll监听eventfd的变化,来阻塞调用pollOnce和恢复调用wake当前线程。
gityuan

native层的NativeMessageQueue实际上并没有做什么实际工作,只是把操作转发给native层的Looper,而native层的Looper则扮演了java层的Handle角色,它可以取出,发送,处理消息。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值