Handler消息机制(native层)
来源:互联网 发布:北京专业关键词优化 编辑:程序博客网 时间:2024/06/05 13:21
Handler消息机制(native层)
- Handler消息机制native层
- 简介
- MessageQueue的native方法
- nativeInit方法
- nativePollOnce方法
- nativeWake方法
- 总结
- Handler消息机制native层
1.简介
在介绍Handler消息机制(Java层)时,我们看到了Java层的MessageQueue中调用了几个native方法。除此之外,native层也有一套完善的消息机制,用于处理native的消息。
首先来看下native层handler消息的类图:
从图中可以看到,native层的消息机制关键元素和Java层的消息机制的关键元素基本一致,分别包括Looper、MessageQueue、Handler、Message这几个关键元素。除了MessageQueue是Java层与Native层相关联的,其余的几个元素都是不相关联的,但元素的功能基本是一致的,只是Java层和Natvie层的实现方式不一样的。
接下来将从MessageQueue中的native方法开始分析Native层的Handler消息机制。
2.MessageQueue的native方法
在Java层的MessageQueue中定义了几个native方法:
public final class 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); ......}
nativeInit方法
在构建Java层的MessageQueue时,会调用nativeInit()方法进行初始化。
2.1 MessageQueue()
MessageQueue(boolean quitAllowed) { mQuitAllowed = quitAllowed; mPtr = nativeInit();// 保存native层的MessageQueue引用,见2.1}
在构建MessageQueue的过程中,会通过JNI调用native层的android_os_MessageQueue_nativeInit方法进行初始化。
2.2 android_os_MessageQueue_nativeInit()
static jlong android_os_MessageQueue_nativeInit(JNIEnv* env, jclass clazz) { NativeMessageQueue* nativeMessageQueue = new NativeMessageQueue();// 构建MessageQueue,见2.3 if (!nativeMessageQueue) { jniThrowRuntimeException(env, "Unable to allocate native queue"); return 0; } nativeMessageQueue->incStrong(env); return reinterpret_cast<jlong>(nativeMessageQueue);//返回到Java层}
2.3 NativeMessageQueue()
NativeMessageQueue::NativeMessageQueue() : mPollEnv(NULL), mPollObj(NULL), mExceptionObj(NULL) { mLooper = Looper::getForThread();//获取该线程关联的Looper if (mLooper == NULL) { mLooper = new Looper(false);// 创建一个Looper对象,见2.4 Looper::setForThread(mLooper);// 将该Looper保存到线程的TLS中 }}
在Native层的MessageQueue中,包含了一个Looper对象,该Looper对象是与线程相关的,保存在线程的TLS中,这个与Java层的处理方式一样。如果该线程的Looper对象存在,则直接返回;如果不存在,则创建一个Looper,并保存到该线程的TLS中。
2.4 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);// 构建一个唤醒事件的文件描述符fdAutoMutex _l(mLock);rebuildEpollLocked();// 重构epoll事件,见2.5}
eventfd函数会创建一个事件对象(eventfd object),用来实现进程(线程)间的等待(wait)/通知(notify)机制(Linux管道pipe也可以实现该功能),内核会为这个对象维护一个64位的计数器。eventfd的函数定义如下:
#include<sys/eventfd.h> int eventfd(unsigned int initval,int flags);
第一个参数initval用来初始化这个计数器,一般初始化为0;
第二个参数为标志位,可选的值为:
- EFD_NONBLOCK:功能同O_NONBLOCK,设置对象为非阻塞状态
- EFD_CLOEXEC:这个标识被设置的话,调用exec后会自动关闭文件描述符,防止泄漏。
在Linux2.6.26版本之前,flags必须设置为0.
eventfd函数返回一个新的文件描述符,这个文件描述符可以支持以下操作:
read:如果计数值counter的值不为0,读取成功,获得到该值。如果counter的值为0,非阻塞模式下,会直接返回失败,并且把errno的值设为EINVAL。如果为阻塞模式,则会一直阻塞到counter为非0为止。
write:将缓存区写入的8字节整形值加到内核计数器上。
在构造native层的Looper时,会通过eventfd函数创建一个唤醒文件描述符,利用IO多路复用机制epoll可以监听该描述符,实现线程间等待/通知。
2.5.Looper.rebuildEpollLocked()
void Looper::rebuildEpollLocked() {// 如果有新的epoll事件,则把原来的epoll事件关闭if (mEpollFd >= 0) { close(mEpollFd);}// Allocate the new epoll instance and register the wake pipe.// 创建一个epoll实例,并注册wake管道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;// 监控的fd为唤醒事件fd// 注册唤醒事件mWakeEventFd到epoll实例中int result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeEventFd, & eventItem);// 如果请求队列中有数据,则还需要将请求中的事件注册到epoll实例中for (size_t i = 0; i < mRequests.size(); i++) { const Request& request = mRequests.valueAt(i); struct epoll_event eventItem; request.initEventItem(&eventItem);// 初始化请求事件,见2.6 // 注册请求中的事件到epoll实例中 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, errno=%d", request.fd, errno); }}}
在rebuildEpollLocked方法中,通过Linux系统的epoll机制,来实现线程间的等待与唤醒操作。Linux系统的epoll机制是为处理大批量句柄而产生的,是Linux下多路复用IO接口select/poll的增强版。epoll除了监控通过eventfd创建的唤醒文件描述符外,还可以监控其他的文件描述符,因为Loope提供了addFd接口来添加文件描述符到Looper对象中,当这些被监控的文件符上有事情发生时,就会唤醒相应的线程来处理。在这里我们只关心通过eventfd创建的唤醒文件描述符。
epoll机制是包含了epoll_create、epoll_ctl和epoll_wait三个方法,通过epoll_create来创建一个epoll专用的文件描述符。通过epoll_ctl函数来告诉需要监控文件描述符的什么事件,这里表示需要监控mWakeEventFd文件描述符的EPOLLIN事件,即当管道中有内容可读时,就唤醒当前正在等待管道中内容的线程。
2.6 Looper.Request.initEventItem()
void Looper::Request::initEventItem(struct epoll_event* eventItem) const { int epollEvents = 0; if (events & EVENT_INPUT) epollEvents |= EPOLLIN;// 如果请求可读,则监听可读事件 if (events & EVENT_OUTPUT) epollEvents |= EPOLLOUT;// 如果请求可写,则监听可写事件 memset(eventItem, 0, sizeof(epoll_event)); // zero out unused members of data field union eventItem->events = epollEvents;// 监听的事件类型 eventItem->data.fd = fd;// 监听的文件描述符fd}
总结:
在nativeInit方法中,主要是创建一个native层的MessageQueue,同时创建一个Looper对象,并在Looper中注册监听wake事件以及Request队列中的事件。当Java层的消息队列中没有消息时,就使Android应用程序主线程进入等待状态,而当Java层的消息队列中来了新的消息后,就唤醒Android应用程序的主线程来处理这个消息。
nativePollOnce方法
在Java层MessageQueue的next方法中,会阻塞调用native层的nativePollOnce方法来获取消息队列中的消息。
3.1 MessageQueue.next()
Message next() { ...... for (;;) { nativePollOnce(ptr, nextPollTimeoutMillis);见3.2 } ......}
3.2 nativePollOnce()
static void android_os_MessageQueue_nativePollOnce(JNIEnv* env, jobject obj, jlong ptr, jint timeoutMillis) { NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr); nativeMessageQueue->pollOnce(env, obj, timeoutMillis);// 调用pollOnce方法,见3.3}
3.3 NativeMessageQueue.pollOnce()
void NativeMessageQueue::pollOnce(JNIEnv* env, jobject pollObj, int timeoutMillis) { mPollEnv = env; mPollObj = pollObj; mLooper->pollOnce(timeoutMillis);// 调用Looper中的pollOnce方法,见3.4 mPollObj = NULL; mPollEnv = NULL; if (mExceptionObj) { env->Throw(mExceptionObj); env->DeleteLocalRef(mExceptionObj); mExceptionObj = NULL; }}
3.4 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) {// 处理没有callback的事件 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);// 内部轮询处理,见3.5}}
pollOnce()方法等待事件准备好,有一个可选的超时时间,timeoutMilllis有以下取值:
- 0,立即返回,没有阻塞;
- 负数,一直阻塞,直到事件发生;
- 正数,表示最多等待多久时间;
pollOnce方法的返回值可以有以下几个取值:
- POLL_WAKE,在超时之前,通过wake()方法唤醒的,没有callbacks被触发,没有文件描述符准备好了,即pipe写端的write事件触发;
- POLL_CALLBACK,在一个或多个文件描述符被触发了;
- POLL_TIMEOUT,在超时之前,没有数据准备好了,表示等待超时;
- POLL_ERROR,发生了错误;
- 大于0的正数,返回数据已经准备好了的fd;
3.5 Looper.pollInner()
int Looper::pollInner(int timeoutMillis) {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_WAKEint result = POLL_WAKE;mResponses.clear();mResponseIndex = 0;// We are about to idle.mPolling = true;struct epoll_event eventItems[EPOLL_MAX_EVENTS];int eventCount = epoll_wait(mEpollFd, 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;}//Poll错误if (eventCount < 0) { if (errno == EINTR) { goto Done; } ALOGW("Poll failed with an unexpected error, errno=%d", errno); result = POLL_ERROR; goto Done;}//Poll超时if (eventCount == 0) { result = POLL_TIMEOUT; goto Done;}// 1.处理所有已经准备好的事件,包含唤醒事件以及Request队列中的事件for (int i = 0; i < eventCount; i++) { int fd = eventItems[i].data.fd;//文件描述符 uint32_t epollEvents = eventItems[i].events;//事件掩码 if (fd == mWakeEventFd) {//如果是唤醒事件 if (epollEvents & EPOLLIN) { awoken();//唤醒,见3.6 } else { ALOGW("Ignoring unexpected epoll events 0x%x on wake event fd.", epollEvents); } } 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; pushResponse(events, mRequests.valueAt(requestIndex));//将请求事件放入Response数组中 } else { ALOGW("Ignoring unexpected epoll events 0x%x on fd %d that is " "no longer registered.", epollEvents, fd); } }}Done: ;// 2.处理所有MessageEnvelopes的消息回调mNextMessageUptime = LLONG_MAX;while (mMessageEnvelopes.size() != 0) { nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); const MessageEnvelope& messageEnvelope = mMessageEnvelopes.itemAt(0); // 如果消息的处理时间小于当前时间,则将该消息从列表中移除 if (messageEnvelope.uptime <= now) { // Remove the envelope from the list. // We keep a strong reference to the handler until the call to handleMessage // finishes. Then we drop it so that the handler can be deleted *before* // we reacquire our lock. { // obtain handler sp<MessageHandler> handler = messageEnvelope.handler;//处理消息的handler Message message = messageEnvelope.message;//获取消息 mMessageEnvelopes.removeAt(0); mSendingMessage = true; mLock.unlock(); handler->handleMessage(message);//处理消息 } // release handler mLock.lock(); mSendingMessage = false; result = POLL_CALLBACK;//事件处理类型为POLL_CALLBACK } else { mNextMessageUptime = messageEnvelope.uptime;//更新下一个消息的处理时间 break; }}// Release lock.mLock.unlock();// 3.处理所有Responses回调for (size_t i = 0; i < mResponses.size(); i++) { Response& response = mResponses.editItemAt(i); // 如果响应类型为POLL_CALLBACK 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);//调用callback类型的handleEvent方法 if (callbackResult == 0) { removeFd(fd, response.request.seq); } response.request.callback.clear(); result = POLL_CALLBACK;//事件处理类型为POLL_CALLBACK }}return result;}
pollInner()方法首先处理epoll_wait()方法返回的准备好的事件,接着处理MessageEnvelopes队列中准备好的事件,最后处理响应Responses队列中的准备好事件。
如果mWakeEventFd文件描述符上发生了EPOLLIN就说明应用程序中的消息队列里面有新的消息需要处理了,接下来它就会先调用awoken函数清空管道中的内容,以便下次再调用pollinner函数时,知道自上次处理完消息队列中的消息后,有没有新的消息加进来。
6.Looper.awoken()
void Looper::awoken() { uint64_t counter; TEMP_FAILURE_RETRY(read(mWakeEventFd, &counter, sizeof(uint64_t)));//不断读取管道数据,目的就是为了清空管道内容}
函数awoken的实现很简单,它只把管道中内容读取出来,清空管道,以便下次调用epoll_wait时可以再次阻塞等待。
总结:在nativePollOnce()过程中,首先处理Response数组中非POLL_CALLBACK类型的事件,接着处理epoll_wait()方法返回准备好的事件,然后是处理MessageEnvelopes队列中事件,最后是处理Response队列中的POLL_CALLBACK类型事件。事件的处理顺序是:
1.Response数组中非POLL_CALLBACK类型的事件 > 2.epoll_wait()返回的wake事件 > 3.处理MessageEnvelopes队列中的事件 > 4.处理Response数组中POLL_CALLBACK类型的事件
当epoll_wait()返回时,可能处理以下几种情况:
- POLL_ERROR,发生了错误;
- POLL_TIMEOUT,发生了超时错误;
- 返回了准备好的事件,有两种类型的事件:一种是唤醒事件,管道已经有数据可读了;另外一种是其他类型的事件,放入到Response数组中处理。
nativeWake方法
nativeWake用于唤醒功能,在添加消息到消息队列enqueueMessage(),或者把消息从消息队列中全部移除quit()时,可能需要调用nativeWake方法。
4.1 MessageQueue.enqueueMessage()
boolean enqueueMessage(Message msg, long when) { ...... if (needWake) { nativeWake(mPtr); } ......}
4.2 android_os_MessageQueue_nativeWake()
static void android_os_MessageQueue_nativeWake(JNIEnv* env, jclass clazz, jlong ptr) { NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr); nativeMessageQueue->wake();//调用NatvieMessageQueue的wake方法,见4.3}
4.3 NativeMessageQueue.wake()
void NativeMessageQueue::wake() { mLooper->wake();//调用Looper的wake方法,见4.4}
4.4 Looper.wake()
void Looper::wake() { uint64_t inc = 1; ssize_t nWrite = TEMP_FAILURE_RETRY(write(mWakeEventFd, &inc, sizeof(uint64_t)));//向管道mWakeEventFd写入字符1 if (nWrite != sizeof(uint64_t)) { if (errno != EAGAIN) { ALOGW("Could not write wake signal, errno=%d", errno); } }}
总结:nativeWake()方法主要是向管道mWakeEventFd中写入字符1,唤醒在mWakeEventFd上等待的事件。往文件描述中写入内容的目的是为了唤醒应用程序的主线程,当应用程序的消息队列中没有消息处理时,应用程序的主线程就会进入空闲等待状态,而这个空闲等待状态就是通过调用Looper类的pollInner函数进入的,具体就是在pollInner函数中调用epoll_wait函数来等待管道中有内容可读的。
当文件描述符有内容可读时,应用程序的主线程就会从Looper的pollInner函数返回到JNI层的nativePollOnce函数,最后返回到Java层的MessageQueue.next函数中去,在这里就会发现消息队列中有新的消息需要处理了,于是就处理这个消息。
3.总结
Java层的MessageQueue通过mPtr变量保存了一个NativeMessageQueue对象,从而使得MessageQueue成为Java层和Native层的衔接桥梁,既能处理上层消息,也能处理native层消息。
Java层和Native层的MessageQueue是通过JNI建立关联的,彼此之间能相互调用。
消息的处理优先级是从先处理Native层的Message,再处理Native层的Request,最后处理Java层的Message。
参考文章:Android消息机制2-Handler(Native层)
Android应用程序消息处理机制(Looper、Handler)分析
- Handler消息机制(native层)
- Android消息机制2-Handler(Native层)
- Handler消息机制(Java层)
- Android Framework学习(八)之Handler消息机制(Native层)解析
- Android消息机制1-Handler(Java层)
- Android消息机制,从Java层到Native层剖析
- 从源码角度分析native层消息机制与java层消息机制的关联
- 源码角度分析native层消息机制与java层消息机制的关联
- Android Framework 分析---2消息机制Native层
- Handler消息传递机制
- Handler 消息传递机制
- handler消息机制
- Handler消息传递机制
- Handler消息传递机制
- Handler消息处理机制
- Handler 消息传递机制
- Android Handler消息机制
- Handler消息机制
- Pycharm基本使用
- Jsonp的原理以及简单实现
- System.Serializable讲解
- Win32图形设计框架基础知识
- HBase分析之Put操作
- Handler消息机制(native层)
- VirtualBox + CentOS 使用 NAT + Host-Only 方式联网
- C语言中字节对齐问题
- LinuxStudyNote(25)-Linux常用命令(4)-文件搜索命令(3)which、whereis命令搜索、grep文件内容查找
- 142. Linked List Cycle II。
- PTA-Shuffling Machine
- C语言 java 百钱百鸡问题
- 音频重采样问题总结
- 第二单元总结