這是Android Input系列的第三篇文章,前面兩篇的地址如下:
今天主要講講App端在收到事件之后,是如何消費(fèi)這些事件的座咆。
首先惩坑,我們看一個事件分發(fā)的典型Java堆棧:
可以看到扛稽,事件是從nativePollOnce
分發(fā)出來的,調(diào)到了InputDispatcherReceiver
的onReceive
方法中,然后再分發(fā)給ViewRootImpl
去處理麸祷。
今天這篇文章,主要講一下App端從socket中收到事件后褒搔,是怎樣調(diào)度到InputDispatcherReceiver.onReceive
方法的阶牍。下一篇文章,我們再講后續(xù)ViewRootImpl
的分發(fā)流程星瘾。
開始之前走孽,先要要說明的是,接收事件的是App端的主線程琳状,最后分發(fā)和處理事件磕瓷,也是在主線程進(jìn)行操作。
之前我們講MessageQueue
的時候說過,主線程會等待在epoll_wait
方法困食,直到監(jiān)聽的端口有內(nèi)容寫入边翁,才會被喚醒,繼續(xù)執(zhí)行下面的流程硕盹。更詳細(xì)的內(nèi)容符匾,可以去看看我之前的文章從epoll機(jī)制看MessageQueue
點(diǎn)擊事件的處理流程就是利用的epoll機(jī)制,就是我們常說的主線程的Looper
機(jī)制瘩例,下面我們一起來詳細(xì)看看源碼啊胶。
epoll機(jī)制監(jiān)聽socketFd
由前面的分析知道,我們在創(chuàng)建了socket
連接后垛贤,會創(chuàng)建一個WindowInputEventReceiver
對象焰坪,并將客戶端的InputChannel
作為構(gòu)造函數(shù)傳入。下面我們就來看看WindowInputEventReceiver
的構(gòu)造方法聘惦。
final class WindowInputEventReceiver extends InputEventReceiver {
//inputChannel是指socket客戶端琳彩,Looper是指UI線程的Looper
public WindowInputEventReceiver(InputChannel inputChannel, Looper looper) {
super(inputChannel, looper);
}
}
WindowInputEventReceiver
繼承自InputEventReceiver
。
public InputEventReceiver(InputChannel inputChannel, Looper looper) {
...
mInputChannel = inputChannel;
mMessageQueue = looper.getQueue(); //UI線程消息隊(duì)列
mReceiverPtr = nativeInit(new WeakReference<InputEventReceiver>(this),
inputChannel, mMessageQueue);
}
InputEventReceiver
調(diào)用的是nativeInit
方法部凑,進(jìn)行初始化
static jlong nativeInit(JNIEnv* env, jclass clazz, jobject receiverWeak, jobject inputChannelObj, jobject messageQueueObj) {
sp<InputChannel> inputChannel = android_view_InputChannel_getInputChannel(env,
inputChannelObj);
//獲取UI主線程的消息隊(duì)列
sp<MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj);
//創(chuàng)建NativeInputEventReceiver對象
sp<NativeInputEventReceiver> receiver = new NativeInputEventReceiver(env,
receiverWeak, inputChannel, messageQueue);
// 調(diào)用setFdEvents露乏,將socket連接的fd添加到主線程Looper的監(jiān)控中
status_t status = receiver->initialize();
return reinterpret_cast<jlong>(receiver.get());
}
在nativeInit
方法中,最終會調(diào)用setFdEvents
方法涂邀,將socket
連接的fd添加到主線程Looper
的監(jiān)控中瘟仿。socket
連接的fd
通過InputChannel
獲取。
void NativeInputEventReceiver::setFdEvents(int events) {
if (mFdEvents != events) {
mFdEvents = events;
int fd = mInputConsumer.getChannel()->getFd();
if (events) {
//將socket客戶端的fd添加到主線程的消息池
mMessageQueue->getLooper()->addFd(fd, 0, events, this, NULL);
} else {
mMessageQueue->getLooper()->removeFd(fd);
}
}
}
addFd
方法比勉,就是通過epoll_ctl
將fd加入監(jiān)聽劳较,同時構(gòu)造一個Request
對象,將它加到mRequests
隊(duì)列中浩聋。
int Looper::addFd(int fd, int ident, int events, const sp<LooperCallback>& callback, void* data) {
{
// 構(gòu)造request
Request request;
request.fd = fd;
request.ident = ident;
request.events = events;
request.seq = mNextRequestSeq++;
request.callback = callback; //是指nativeInputEventReceiver
request.data = data;
// 構(gòu)造eventItem
struct epoll_event eventItem;
request.initEventItem(&eventItem);
ssize_t requestIndex = mRequests.indexOfKey(fd);
if (requestIndex < 0) {
//通過epoll監(jiān)聽fd
int epollResult = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, fd, & eventItem);
//該fd的request加入到mRequests隊(duì)列
mRequests.add(fd, request);
} else {
int epollResult = epoll_ctl(mEpollFd, EPOLL_CTL_MOD, fd, & eventItem);
mRequests.replaceValueAt(requestIndex, request);
}
}
return 1;
}
我們來看看Request
的結(jié)構(gòu):
- fd:存的是socket通信的fd
- ident:0
- events:ALOOPER_EVENT_INPUT
- callback:就是nativeInputEventReceiver
epoll_wait被喚醒
當(dāng)監(jiān)聽的socket
收到數(shù)據(jù)時观蜗,會從pollInner
方法喚醒主線程Looper
處理消息。
int Looper::pollInner(int timeoutMillis) {
mPolling = true; //即將處于idle狀態(tài)
struct epoll_event eventItems[EPOLL_MAX_EVENTS]; //fd最大個數(shù)為16
//等待事件發(fā)生或者超時衣洁,在nativeWake()方法墓捻,向管道寫端寫入字符;
int eventCount = epoll_wait(mEpollFd, eventItems, EPOLL_MAX_EVENTS, timeoutMillis);
mPolling = false; //不再處于idle狀態(tài)
//循環(huán)遍歷,處理所有的事件
for (int i = 0; i < eventCount; i++) {
int fd = eventItems[i].data.fd;
uint32_t epollEvents = eventItems[i].events;
// 如果是從wakeEventFd喚醒坊夫,表示MessageQueue有新消息了砖第,會往這個fd寫入
if (fd == mWakeEventFd) {
if (epollEvents & EPOLLIN) {
// 這個方法會讀取mWakeEventFd上的所有數(shù)據(jù)
awoken();
}
} else {
ssize_t requestIndex = mRequests.indexOfKey(fd);
if (requestIndex >= 0) {
//處理request,生成對應(yīng)的reponse對象环凿,push到mResponses數(shù)組
pushResponse(events, mRequests.valueAt(requestIndex));
}
}
}
Done: ;
//處理帶有Callback()方法的Response事件梧兼,執(zhí)行Reponse相應(yīng)的回調(diào)方法
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;
// 處理請求的回調(diào)方法
int callbackResult = response.request.callback->handleEvent(fd, events, data);
// 正常處理事件會返回1,如果返回0智听,表示窗口被移除
if (callbackResult == 0) {
removeFd(fd, response.request.seq); //移除fd
}
response.request.callback.clear(); //清除reponse引用的回調(diào)方法
result = POLL_CALLBACK; // 發(fā)生回調(diào)
}
}
return result;
}
這個方法的流程:
- 獲取喚醒的fd和events羽杰,從
mRequest
中找到對應(yīng)fd的request
- 將events和request封裝成一個
response
對象渡紫,然后將他加到mResponses
數(shù)組中 - 循環(huán)處理
mResponses
數(shù)組中的所有response,調(diào)用request.callback->handleEvent
這個方法會調(diào)到request.callback->handleEvent
考赛,也就是NativeInputEventReceiver
的handleEvent
方法腻惠。這個方法主要調(diào)用了consumeEvents
,所以我們直接看ConsumeEvents
方法欲虚。
讀取并消費(fèi)所有的message
status_t NativeInputEventReceiver::consumeEvents(JNIEnv* env,
bool consumeBatches, nsecs_t frameTime, bool* outConsumedBatch) {
// 循環(huán)消費(fèi)所有的消息
for (;;) {
status_t status = mInputConsumer.consume(&mInputEventFactory,
consumeBatches, frameTime, &seq, &inputEvent);
if (inputEventObj) {
//執(zhí)行Java層的InputEventReceiver.dispachInputEvent
env->CallVoidMethod(receiverObj.get(),
gInputEventReceiverClassInfo.dispatchInputEvent, seq, inputEventObj);
} else {
skipCallbacks = true;
}
}
if (skipCallbacks) {
//發(fā)生異常集灌,則直接向InputDispatcher線程發(fā)送完成信號。
mInputConsumer.sendFinishedSignal(seq, false);
}
}
}
循環(huán)讀取所有的消息复哆,并且調(diào)用Java層的InputEventReceiver.dispatchInputEvent
方法處理事件欣喧。
status_t InputConsumer::consume(InputEventFactoryInterface* factory,
bool consumeBatches, nsecs_t frameTime, uint32_t* outSeq, InputEvent** outEvent) {
//循環(huán)遍歷所有的Event
while (!*outEvent) {
if (mMsgDeferred) {
mMsgDeferred = false; //上一次沒有處理的消息
} else {
// 通過InputChannel接收一條消息
status_t result = mChannel->receiveMessage(&mMsg);
if (result) {
if (consumeBatches || result != WOULD_BLOCK) {
result = consumeBatch(factory, frameTime, outSeq, outEvent);
}
}
}
}
}
return OK;
}
單條消息,調(diào)用InputChannel
的receiveMessage
讀取梯找。
status_t InputChannel::receiveMessage(InputMessage* msg) {
ssize_t nRead;
do {
//讀取InputDispatcher發(fā)送過來的消息
nRead = ::recv(mFd, msg, sizeof(InputMessage), MSG_DONTWAIT);
} while (nRead == -1 && errno == EINTR);
return OK;
}
Java端收到事件后唆阿,會回調(diào)到WindowInputEventReceiver
的onInputEvent
方法中,處理事件锈锤。
發(fā)送處理完信號
當(dāng)事件處理完后驯鳖,會調(diào)用finishInputEvent
方法,將處理完的結(jié)果返回給系統(tǒng)久免。
從Java端的InputEventReceiver
開始浅辙。
public final void finishInputEvent(InputEvent event, boolean handled) {
int seq = mSeqMap.valueAt(index);
mSeqMap.removeAt(index);
// 調(diào)用native的方法
nativeFinishInputEvent(mReceiverPtr, seq, handled);
}
調(diào)用到NativeInputEventReceiver
,最終調(diào)用到sendFinishedSignal
阎姥,然后調(diào)用到sendUnchainedFinishedSignal
记舆。
status_t InputConsumer::sendUnchainedFinishedSignal(uint32_t seq, bool handled) {
// 組裝一個finished的信號,通過InputChannel的socket發(fā)送給系統(tǒng)
InputMessage msg;
msg.header.type = InputMessage::TYPE_FINISHED;
msg.body.finished.seq = seq;
msg.body.finished.handled = handled;
return mChannel->sendMessage(&msg);
}
總結(jié)
App端消費(fèi)事件的流程如下:
- 在創(chuàng)建完
socketpair
后呼巴,App端會用mInputChanne
l創(chuàng)建一個WindowInputEventReceiver
對象泽腮,并且注冊對socket fd
的監(jiān)聽。 - 當(dāng)
socket fd
上有輸入寫入時(即有事件時)衣赶,會喚醒主線程 - 主線程循環(huán)讀取socket fd上的
InputMessage
诊赊,然后將message
發(fā)送給Java層的InputEventReceiver
去處理 - 處理完之后,組裝一個
finished
的信號府瞄,通過mInputChannel
發(fā)送給system_server