在Android消息处理机制中,不仅包括了Java层的消息机制处理,还包括了Native消息处理机制(也拥有Handler、Looper、MessageQueue)
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 函数等待
-
- 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角色,它可以取出,发送,处理消息。