【轉載兩篇關於android按鍵事件傳遞的文章,流程非常完整】Android按鍵事件傳遞流程(二)
5 應用層如何從Framework層接收按鍵事件
由3.2和4.5.4節可知,當InputDispatcher通過服務端管道向socket檔案描述符傳送訊息後,epoll機制監聽到了I/O事件,epoll_wait就會執行返回發生事件的個數給eventCount,主執行緒開始執行epoll_wait後面的程式碼:
fd是客戶端socket檔案描述符,不是mWakeReadPipeFd,因此if語句不成立,進入else子句。mRequests不為空(3.4.2.2節中已經把Request儲存在了mRequests中),pushResponse函式把request取出來賦給response,再放到mResponses容器中儲存。
mMessageEnvelopes被初始化為空,也沒有加入資料,依然為空,這句:
while (mMessageEnvelopes.size() != 0) {
不成立,跳過while迴圈。
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;
#if DEBUG_POLL_AND_WAKE || DEBUG_CALLBACKS
ALOGD("%p ~ pollOnce - invoking fd event callback %p: fd=%d, events=0x%x, data=%p",
this, response.request.callback.get(), fd, events, data);
#endif
int callbackResult = response.request.callback->handleEvent(fd, events, data);
if (callbackResult == 0) {
removeFd(fd);
}
// Clear the callback reference in the response structure promptly because we
// will not clear the response vector itself until the next poll.
response.request.callback.clear();
result = POLL_CALLBACK;
}
mResponses不為空,for迴圈取出裡面的response物件,然後執行:
int callbackResult = response.request.callback->handleEvent(fd, events, data);
fd就是服務端socket檔案描述符,events是發生的事件,data為空,response.request.callback就是addFd中的第4個實參NativeInputEventReceiver物件也是LooperCallback物件,這句話就是回撥主執行緒中的NativeInputEventReceiver物件的handleEvent函式
5.1 NativeInputEventReceiver的handleEvent
int NativeInputEventReceiver::handleEvent(int receiveFd, int events, void* data) {
if (events & (ALOOPER_EVENT_ERROR | ALOOPER_EVENT_HANGUP)) {
#if DEBUG_DISPATCH_CYCLE
// This error typically occurs when the publisher has closed the input channel
// as part of removing a window or finishing an IME session, in which case
// the consumer will soon be disposed as well.
ALOGD("channel '%s' ~ Publisher closed input channel or an error occurred. "
"events=0x%x", getInputChannelName(), events);
#endif
return 0; // remove the callback
}
if (events & ALOOPER_EVENT_INPUT) {
JNIEnv* env = AndroidRuntime::getJNIEnv();
status_tstatus = consumeEvents(env, false /*consumeBatches*/, -1, NULL);
mMessageQueue->raiseAndClearException(env, "handleReceiveCallback");
return status == OK || status == NO_MEMORY ? 1 : 0;
}
if (events & ALOOPER_EVENT_OUTPUT) {
for (size_t i = 0; i < mFinishQueue.size(); i++) {
const Finish& finish = mFinishQueue.itemAt(i);
status_tstatus = mInputConsumer.sendFinishedSignal(finish.seq, finish.handled);
if (status) {
mFinishQueue.removeItemsAt(0, i);
if (status == WOULD_BLOCK) {
#if DEBUG_DISPATCH_CYCLE
ALOGD("channel '%s' ~ Sent %u queued finish events; %u left.",
getInputChannelName(), i, mFinishQueue.size());
#endif
return 1; // keep the callback, try again later
}
ALOGW("Failed to send finished signal on channel '%s'. status=%d",
getInputChannelName(), status);
if (status != DEAD_OBJECT) {
JNIEnv* env = AndroidRuntime::getJNIEnv();
String8message;
message.appendFormat("Failed to finish input event. status=%d", status);
jniThrowRuntimeException(env, message.string());
mMessageQueue->raiseAndClearException(env, "finishInputEvent");
}
return 0; // remove the callback
}
}
#if DEBUG_DISPATCH_CYCLE
ALOGD("channel '%s' ~ Sent %u queued finish events; none left.",
getInputChannelName(), mFinishQueue.size());
#endif
mFinishQueue.clear();
setFdEvents(ALOOPER_EVENT_INPUT);
return 1;
}
ALOGW("channel '%s' ~ Received spurious callback for unhandled poll event. "
"events=0x%x", getInputChannelName(), events);
return 1;
}
先對events事件程序必要的檢查,如果包含ALOOPER_EVENT_ERROR或ALOOPER_EVENT_HANGUP表示管道關閉,這種情況下丟棄事件
if (events & ALOOPER_EVENT_INPUT) {
JNIEnv* env = AndroidRuntime::getJNIEnv();
status_tstatus = consumeEvents(env, false /*consumeBatches*/, -1, NULL);
mMessageQueue->raiseAndClearException(env, "handleReceiveCallback");
return status == OK || status == NO_MEMORY ? 1 : 0;
}
如果為ALOOPER_EVENT_INPUT事件,呼叫consumeEvents繼續執行;如果為ALOOPER_EVENT_OUTPUT表示客戶端已經收到資料,需要傳送一個完成訊號給服務端
5.2 NativeInputEventReceiver的consumeEvents
status_tNativeInputEventReceiver::consumeEvents(JNIEnv* env,
bool consumeBatches, nsecs_tframeTime, bool* outConsumedBatch) {
#if DEBUG_DISPATCH_CYCLE
ALOGD("channel '%s' ~ Consuming input events, consumeBatches=%s, frameTime=%lld.",
getInputChannelName(), consumeBatches ? "true" : "false", frameTime);
#endif
if (consumeBatches) {
mBatchedInputEventPending = false;
}
if (outConsumedBatch) {
*outConsumedBatch = false;
}
ScopedLocalRef<jobject> receiverObj(env, NULL);
bool skipCallbacks = false;
for (;;) {
uint32_tseq;
InputEvent* inputEvent;
status_tstatus = mInputConsumer.consume(&mInputEventFactory,
consumeBatches, frameTime, &seq, &inputEvent);
if (status) {
if (status == WOULD_BLOCK) {
if (!skipCallbacks && !mBatchedInputEventPending
&& mInputConsumer.hasPendingBatch()) {
// There is a pending batch. Come back later.
if (!receiverObj.get()) {
receiverObj.reset(jniGetReferent(env, mReceiverWeakGlobal));
if (!receiverObj.get()) {
ALOGW("channel '%s' ~ Receiver object was finalized "
"without being disposed.", getInputChannelName());
return DEAD_OBJECT;
}
}
mBatchedInputEventPending = true;
#if DEBUG_DISPATCH_CYCLE
ALOGD("channel '%s' ~ Dispatching batched input event pending notification.",
getInputChannelName());
#endif
env->CallVoidMethod(receiverObj.get(),
gInputEventReceiverClassInfo.dispatchBatchedInputEventPending);
if (env->ExceptionCheck()) {
ALOGE("Exception dispatching batched input events.");
mBatchedInputEventPending = false; // try again later
}
}
return OK;
}
ALOGE("channel '%s' ~ Failed to consume input event. status=%d",
getInputChannelName(), status);
return status;
}
assert(inputEvent);
if (!skipCallbacks) {
if (!receiverObj.get()) {
receiverObj.reset(jniGetReferent(env, mReceiverWeakGlobal));
if (!receiverObj.get()) {
ALOGW("channel '%s' ~ Receiver object was finalized "
"without being disposed.", getInputChannelName());
return DEAD_OBJECT;
}
}
jobjectinputEventObj;
switch (inputEvent->getType()) {
case AINPUT_EVENT_TYPE_KEY:
#if DEBUG_DISPATCH_CYCLE
ALOGD("channel '%s' ~ Received key event.", getInputChannelName());
#endif
inputEventObj = android_view_KeyEvent_fromNative(env,
static_cast<KeyEvent*>(inputEvent));
break;
case AINPUT_EVENT_TYPE_MOTION: {
#if DEBUG_DISPATCH_CYCLE
ALOGD("channel '%s' ~ Received motion event.", getInputChannelName());
#endif
MotionEvent* motionEvent = static_cast<MotionEvent*>(inputEvent);
if ((motionEvent->getAction() & AMOTION_EVENT_ACTION_MOVE) && outConsumedBatch) {
*outConsumedBatch = true;
}
inputEventObj = android_view_MotionEvent_obtainAsCopy(env, motionEvent);
break;
}
default:
assert(false); // InputConsumer should prevent this from ever happening
inputEventObj = NULL;
}
if (inputEventObj) {
#if DEBUG_DISPATCH_CYCLE
ALOGD("channel '%s' ~ Dispatching input event.", getInputChannelName());
#endif
env->CallVoidMethod(receiverObj.get(),
gInputEventReceiverClassInfo.dispatchInputEvent, seq, inputEventObj);
if (env->ExceptionCheck()) {
ALOGE("Exception dispatching input event.");
skipCallbacks = true;
}
env->DeleteLocalRef(inputEventObj);
} else {
ALOGW("channel '%s' ~ Failed to obtain event object.", getInputChannelName());
skipCallbacks = true;
}
}
if (skipCallbacks) {
mInputConsumer.sendFinishedSignal(seq, false);
}
}
}
在for迴圈中呼叫InputConsumer物件的consume函式從socket客戶端獲取InputDispatcher傳送來的事件儲存到inputEvent物件中,先分析consume方法,然後返回來再看後面的程式碼
在consume函式中有這句:
status_tresult = mChannel->receiveMessage(&mMsg);
receiveMessage的原始碼中有這句:
在3.2節通過send向服務端socket傳送了資料,那麼在此處通過recv從客戶端socket上獲取的訊息並儲存到mMsg中,如果成功,再根據事件型別選擇case語句:
switch (mMsg.header.type) {
case InputMessage::TYPE_KEY: {
KeyEvent* keyEvent = factory->createKeyEvent();
if (!keyEvent) return NO_MEMORY;
initializeKeyEvent(keyEvent, &mMsg);
*outSeq = mMsg.body.key.seq;
*outEvent = keyEvent;
#if DEBUG_TRANSPORT_ACTIONS
ALOGD("channel '%s' consumer ~ consumed key event, seq=%u",
mChannel->getName().string(), *outSeq);
#endif
break;
}
把訊息儲存到KeyEvent物件中,再付給outEvent,如果一切成功,返回OK到5.2節這句mInputConsumer.consume的後面繼續執行:
mReceiverWeakGlobal就是傳遞過來的WindowInputEventReceiver物件的本地引用
jobjectinputEventObj;
switch (inputEvent->getType()) {
case AINPUT_EVENT_TYPE_KEY:
#if DEBUG_DISPATCH_CYCLE
ALOGD("channel '%s' ~ Received key event.", getInputChannelName());
#endif
inputEventObj = android_view_KeyEvent_fromNative(env,
static_cast<KeyEvent*>(inputEvent));
break;
如果讀取所有事件成功,再根據事件的型別選擇相應執行語句,如果是按鍵事件,就呼叫android_view_KeyEvent_fromNative把按鍵事件傳遞給Java層KeyEvent物件並初始化,然後返回該物件賦給變數inputEventObj,繼續執行到這句:
receiverObj.get()得到WindowInputEventReceiver物件,gInputEventReceiverClassInfo.dispatchInputEvent就是待調方法的id號,CallVoidMethod函式的作用就是呼叫第一個引數WindowInputEventReceiver物件的dispatchInputEvent方法,後面兩個實參會傳遞給該方法,如果這一切都成功,就把按鍵事件傳遞到java層處理。
由於WindowInputEventReceiver中沒有實現dispatchInputEvent,因此直接呼叫父類InputEventReceiver的dispatchInputEvent方法
5.3 InputEventReceiver的dispatchInputEvent
onInputEvent在WindowInputEventReceiver中已經實現,就呼叫WindowInputEventReceiver中的onInputEvent方法,onInputEvent呼叫了enqueueInputEvent
5.4 ViewRootImpl的enqueueInputEvent
enqueueInputEvent(event, this, 0, true);
void enqueueInputEvent(InputEventevent,
InputEventReceiverreceiver, int flags, boolean processImmediately) {
QueuedInputEvent q = obtainQueuedInputEvent(event, receiver, flags);
// Always enqueue the input event in order, regardless of its time stamp.
// We do this because the application or the IME may inject key events
// in response to touch events and we want to ensure that the injected keys
// are processed in the order they were received and we cannot trust that
// the time stamp of injected events are monotonic.
QueuedInputEventlast = mPendingInputEventTail;
if (last == null) {
mPendingInputEventHead = q;
mPendingInputEventTail = q;
} else {
last.mNext = q;
mPendingInputEventTail = q;
}
mPendingInputEventCount += 1;
Trace.traceCounter(Trace.TRACE_TAG_INPUT, mPendingInputEventQueueLengthCounterName,
mPendingInputEventCount);
if (processImmediately) {
doProcessInputEvents();
} else {
scheduleProcessInputEvents();
}
}
obtainQueuedInputEvent方法把輸入事件封裝成QueuedInputEvent物件並放入到QueuedInputEvent物件池中,然後取出一個QueuedInputEvent事件後按照次序排列,再呼叫doProcessInputEvents方法從佇列中迴圈取出事件傳送給輸入法或者應用程式等不同階段進行處理。
doProcessInputEvents的呼叫過程:
doProcessInputEvents —-> deliverInputEvent —-> stage.deliver(q)
public final void deliver(QueuedInputEvent q) {
if ((q.mFlags & QueuedInputEvent.FLAG_FINISHED) != 0) {
forward(q);
} else if (shouldDropInputEvent(q)) {
finish(q, false);
} else {
apply(q, onProcess(q));
}
}
當前事件還沒有處理,因此不包含FLAG_FINISHED標緻,if語句不成立;正常情況下不會丟棄當前事件,第一個else子句也不成立,執行最後一個else子句。apply的第二個引數是onProcess方法,ViewRootImpl中有8個onProcess方法,具體呼叫哪個?
在setView方法最後建立了很多InputStage物件:
InputStage是抽象基類,ViewPostImeInputStage,EarlyPostImeInputStage,ViewPreImeInputStage等物件都是為了處理輸入事件在不同階段而建立的,比如:ViewPostImeInputStage表示傳送輸入事件給view樹進行處理,這些輸入事件都是在輸入法處理之後的。ViewPreImeInputStage表示輸入事件必須在輸入法處理之前傳送給view樹處理。
ViewPreImeInputStage表示在輸入法之前處理,ImeInputStage表示進入輸入法處理,ViewPostImeInputStage表示傳送給檢視。如果有輸入法視窗,就先傳輸給ViewPreImeInputStage處理,如果沒有,傳輸給ViewPostImeInputStage,一般情況下,都是傳給ViewPostImeInputStage。
此處會呼叫ViewPostImeInputStage的onProcess來處理:
if語句成立,呼叫processKeyEvent
5.5 ViewPostImeInputStage的processKeyEvent