引言
我们知道,线程在没有消息可处理时会使用 MessageQueue#nativePollOnce() 陷入阻塞。当我们使用 Handler#sendMessage() 时又会通过 MessageQueue#nativeWake() 唤醒当前线程。
我们又知道,ims 通过 socket 将 touch 事件发往应用进程,应用进程收到后也会唤醒主线程,然后执行 InputEventReceiver#dispatchInputEvent(),并不会继续执行 MessageQueue#next。
现在有一个问题:touch 事件唤醒与通过 nativeWake() 的唤醒有什么区别?为什么主线程被 touch 事件唤醒后不会执行 MessageQueue#next?
MessageQueue
java 层的 MQ 在构造函数中会调用 nativeInit(),进入到 native 层。native 层做的事也很简单:创建一个 NativeMessageQueue 对象,并返回它的地址由 java 层 MQ#mPtr 记录。
NativeMessageQueue 在构造函数中会创建一个 Looper 对象,并使用 mLooper 指向该对象。注意这里 Looper 与 java 层的 Looper 没有任务关系
NativeMessageQueue::NativeMessageQueue() :
mPollEnv(NULL), mPollObj(NULL), mExceptionObj(NULL) {
// 理解成 java 层的 ThreadLocal 即可
mLooper = Looper::getForThread();
if (mLooper == NULL) {
mLooper = new Looper(false);
Looper::setForThread(mLooper);
}
}
Looper 本身可以理解为对 epoll 机制的封装。它的构造函数如下:
Looper::Looper(bool allowNonCallbacks)
: mAllowNonCallbacks(allowNonCallbacks),
mSendingMessage(false),
mPolling(false),
mEpollRebuildRequired(false),
mNextRequestSeq(WAKE_EVENT_FD_SEQ + 1),
mResponseIndex(0),
mNextMessageUptime(LLONG_MAX) {
// 重置 mWakeEventFd
// 这个 fd 很重要,往该 fd 中写入数据可以唤醒 looper
mWakeEventFd.reset(eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC));
rebuildEpollLocked();
}
void Looper::rebuildEpollLocked() {
//...
// 使用 epoll_create1,创建 epoll 句柄
mEpollFd.reset(epoll_create1(EPOLL_CLOEXEC));
epoll_event wakeEvent = createEpollEvent(EPOLLIN, WAKE_EVENT_FD_SEQ);
// 将 mWakeEventFd 添加到 epoll 的监听列表中
// 这样通过 mWakeEventFd 写入数据时就可以唤醒 epoll
int result = epoll_ctl(mEpollFd.get(), EPOLL_CTL_ADD, mWakeEventFd.get(), &wakeEvent);
// 将一些在创建前就设置的 fd 也监听上,
for (const auto& [seq, request] : mRequests) {
epoll_event eventItem = createEpollEvent(request.getEpollEvents(), seq);
int epollResult = epoll_ctl(mEpollFd.get(), EPOLL_CTL_ADD, request.fd, &eventItem);
}
}
上面就是整个 MessageQueue 的创建过程,下面看一下 java 层 MessageQueue 中一些方法的实现。
nativePollOnce 与 nativeWake
nativePollOnce() 在 MQ#next() 中调用,nativeWake() 在 MQ#enqueueMessage() 中调用。在 native 层两个方法都是调用了 NativeMessageQueue 的同名方法,再由 NativeMQ 调用到 Looper::pollOnce() 以及 Looper::wake()
Looper::pollOnce() 经过一系列调用到 pollInner(),这里面省略一些内容:
int Looper::pollInner(int timeoutMillis) {
int result = POLL_WAKE;
mResponses.clear();
mResponseIndex = 0;
// We are about to idle.
mPolling = true;
struct epoll_event eventItems[EPOLL_MAX_EVENTS];
// 调用 epoll_wait,线程陷入等待。
// 该方法结束时,eventCount 返回的是 epoll 监听的 fd 列表中有多少个 fd 有事件到来
// 并会将 fd 放到传入的 eventItems 中
int eventCount = epoll_wait(mEpollFd.get(), eventItems, EPOLL_MAX_EVENTS, timeoutMillis);
mPolling = false;
// 遍历 eventItems,处理每一个事件
for (int i = 0; i < eventCount; i++) {
const SequenceNumber seq = eventItems[i].data.u64;
uint32_t epollEvents = eventItems[i].events;
if (seq == WAKE_EVENT_FD_SEQ) {
// WAKE_EVENT_FD_SEQ = 1,对应的是 mWakeEventFd
// 如果是通过 mWakeEventFd 唤醒的,就走该分支
if (epollEvents & EPOLLIN) {
// 判断成立,说明 mWakeEventFd 中有写入事件
// 这个方法没有什么逻辑,主要就是将 mWakeEventFd 中写入的数据读出来
awoken();
}
} else {
// 否则就是一些别的 fd
const auto& request_it = mRequests.find(seq);
if (request_it != mRequests.end()) {
const auto& request = request_it->second;
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;
//
mResponses.push({.seq = seq, .events = events, .request = request});
} else {
}
}
}
mLock.unlock();
// 遍历处理所有的 response
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;
// 调用其 handleEvent() 方法
int callbackResult = response.request.callback->handleEvent(fd, events, data);
if (callbackResult == 0) {
// handleEvent 返回 0,则将 request 移除掉,即下次再也收不到事件了
removeSequenceNumberLocked(response.seq);
}
response.request.callback.clear();
result = POLL_CALLBACK;
}
}
return result;
}
pollInner 内容使用 epoll_wait 进行等待。epoll 被唤醒后会根据要唤醒的 fd 执行不同的逻辑:
- 如果是 mWakeEventFd 中有数据,那什么操作也不做,线程自然进行 MessageQueue#next() 中继续执行
- 否则就调用 handleEvent()。这一块逻辑后面看 addFd() 时再说。
下面再看 wake(),即 MessageQueue#enqueueMessage 中调用的。核心逻辑就一句话:
void Looper::wake() {
// 通过 write() 向 mWakeEventFd 中写入数据
// 结合上面的 pollInner() 可知,这边写入后 epoll 就会读到,进而唤醒线程
ssize_t nWrite = TEMP_FAILURE_RETRY(write(mWakeEventFd.get(), &inc, sizeof(uint64_t)));
}
到此,native 层的基本逻辑已分析完毕,主要还是利用了 epoll 机制,所谓唤醒,也就是 epoll 预监听一个 fd,唤醒时往该 fd 中写入一个数据即可
addFd
我们还知道,ims 是 touch 事件的源头,它经过一系列处理后通过 socket 传给应用进程,它使用 socket 是 wms 创建的:wms 使用 socketpair() 创建一对套接在一起的 socket,一个给 InputDispatcher,一个返回给应用进程。ims 往留给 InputDispatcher 的 socket 中写入数据,那应用进程就可以收到。
现在看应用进程自己的处理,应用进程拿到 socket 后(当然,wms 并不是直接返回的 socket,只是最核心的还是 socket),最终会调用到 NativeInputEventReceiver::setFdEvents():
void NativeInputEventReceiver::setFdEvents(int events) {
// 省略其余,最核心的就是将 socket 对应的 fd 添加到自己主线程的 Looper 中
mMessageQueue->getLooper()->addFd(fd, 0, events, this, nullptr);
}
下面就到了 Looper::addFd()
int Looper::addFd(int fd, int ident, int events, const sp<LooperCallback>& callback, void* data) {
{
// 先给一个序列号。我们上面提到的 mWakeEventFd,它的序列号是 1
const SequenceNumber seq = mNextRequestSeq++;
// 封装成 request
Request request;
request.fd = fd;
request.ident = ident;
request.events = events;
request.callback = callback;
request.data = data;
epoll_event eventItem = createEpollEvent(request.getEpollEvents(), seq);
auto seq_it = mSequenceNumberByFd.find(fd);
if (seq_it == mSequenceNumberByFd.end()) {
// 通过 epoll_ctl 向 epoll 中添加对 fd 的监听
int epollResult = epoll_ctl(mEpollFd.get(), EPOLL_CTL_ADD, fd, &eventItem);
} else {
}
} // release lock
return 1;
}
这就是 touch 事件能唤醒主线程的原因:因为主线程的 looper 也监听了 touch 事件对应的 socket。将 addFd() 与上面的 pollOnce() 结合着看,更好理解。
总结
到这里,native 层相关的逻辑已结束,核心还是 epoll
唤醒:epoll 预监听一个 fd,需要唤醒时向该 fd 中写数据即可