窗口获取输入事件
③. 窗口获取输入事件
- 经过上面的分析,此时 InputMessage 对象借由 server 端 InputChannel 的 Socket 对象发送到了 Client 端 InputChannel 的 Socket 处,而 Client 端 InputChannel 添加了 Socket 的监听,当 存在消息时,就会调用
NativeInputEventReceiver::handleEvent()
函数进行处理,那么接下来我们就从此处开始进行梳理接下来的键盘事件传递流程
⑴. handleEvent()
android/frameworks/…/android_view_InputEventReceiver.cpp
int NativeInputEventReceiver::handleEvent(int receiveFd, int events, void* data) {
//如果当前输入事件异常,则直接返回 0
if (events & (ALOOPER_EVENT_ERROR | ALOOPER_EVENT_HANGUP)) {
if (kDebugDispatchCycle) {
ALOGD("channel '%s' ~ Publisher closed input channel or an error occurred. "
"events=0x%x", getInputChannelName().c_str(), events);
}
return 0; // remove the callback
}
//首先会命中该if,表示应用程序开始准备分发该输入事件
if (events & ALOOPER_EVENT_INPUT) {
JNIEnv* env = AndroidRuntime::getJNIEnv();
//消费按键事件
status_t status = consumeEvents(env, false /*consumeBatches*/, -1, nullptr);
mMessageQueue->raiseAndClearException(env, "handleReceiveCallback");
return status == OK || status == NO_MEMORY ? 1 : 0;
}
//命中该if,表示当前应用程序已经完成了输入事件的分发,我们暂且先不看这部分内容
if (events & ALOOPER_EVENT_OUTPUT) {
...
}
ALOGW("channel '%s' ~ Received spurious callback for unhandled poll event. "
"events=0x%x", getInputChannelName().c_str(), events);
return 1;
}
- 可以看到,处理输入事件会调用
consumeEvents()
函数进行,至于对应的完成 输入事件处理的逻辑,我们当前暂且不分析
1. consumeEvents()
android/frameworks/…/android_view_InputEventReceiver.cpp
//入参:consumeBatches = false; frameTime = -1;outConsumedBatch = null
status_t NativeInputEventReceiver::consumeEvents(JNIEnv* env,
bool consumeBatches, nsecs_t frameTime, bool* outConsumedBatch) {
if (kDebugDispatchCycle) {
ALOGD("channel '%s' ~ Consuming input events, consumeBatches=%s, frameTime=%" PRId64,
getInputChannelName().c_str(), toString(consumeBatches), frameTime);
}
if (consumeBatches) {
mBatchedInputEventPending = false;
}
if (outConsumedBatch) {
*outConsumedBatch = false;
}
ScopedLocalRef<jobject> receiverObj(env, nullptr);
bool skipCallbacks = false;
for (;;) {
uint32_t seq;
InputEvent* inputEvent;
//调用 consume() 从 InputChannel 中获取到输入事件,并存放到 inputEvent 变量中
status_t status = mInputConsumer.consume(&mInputEventFactory,
consumeBatches, frameTime, &seq, &inputEvent);
if (status != OK && status != WOULD_BLOCK) {
ALOGE("channel '%s' ~ Failed to consume input event. status=%d",
getInputChannelName().c_str(), status);
return status;
}
...
//构建出来的 skipCallbacks == false,所以会命中if
if (!skipCallbacks) {
...
switch (inputEvent->getType()) {
case AINPUT_EVENT_TYPE_KEY:
if (kDebugDispatchCycle) {
ALOGD("channel '%s' ~ Received key event.", getInputChannelName().c_str());
}
//将 inputEvent 对象从 C++ 转成 JAVA 层的对象
inputEventObj = android_view_KeyEvent_fromNative(env,
static_cast<KeyEvent*>(inputEvent));
break;
...
}
//构建的 JAVA 层 keyevent 对象不为空
if (inputEventObj) {
if (kDebugDispatchCycle) {
ALOGD("channel '%s' ~ Dispatching input event.", getInputChannelName().c_str());
}
//调用 InputEventReceiver.java 对象的 dispatchInputEvent() 函数继续处理该 输入事件
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().c_str());
skipCallbacks = true;
}
}
//当命中该if时,会发送结束信号
if (skipCallbacks) {
mInputConsumer.sendFinishedSignal(seq, false);
}
}
}
-
在该函数中,会先通过
InputTransport$consume()
函数从 InputChannel 中取出 输入事件,并将其封装成 InputEvent 对象,简要看一下获取过程:status_t InputConsumer::consume(InputEventFactoryInterface* factory, bool consumeBatches, nsecs_t frameTime, uint32_t* outSeq, InputEvent** outEvent) { if (DEBUG_TRANSPORT_ACTIONS) { ALOGD("channel '%s' consumer ~ consume: consumeBatches=%s, frameTime=%" PRId64, mChannel->getName().c_str(), toString(consumeBatches), frameTime); } *outSeq = 0; *outEvent = nullptr; while (!*outEvent) { if (mMsgDeferred) { mMsgDeferred = false; } else { //从 InputChannel 中取出按键事件,其实就是通过 socket 去进行读取,读取出来的输入事件存放在 mMsg 变量中 status_t result = mChannel->receiveMessage(&mMsg); //获取输入事件不成功,那么就不会命中if,就直接返回了 if (result) { if (consumeBatches || result != WOULD_BLOCK) { result = consumeBatch(factory, frameTime, outSeq, outEvent); if (*outEvent) { if (DEBUG_TRANSPORT_ACTIONS) { ALOGD("channel '%s' consumer ~ consumed batch event, seq=%u", mChannel->getName().c_str(), *outSeq); } break; } } return result; } } switch (mMsg.header.type) { case InputMessage::Type::KEY: { //factory参数是入参,其实就是 PreallocatedInputEventFactory 对象 //此处返回的是 KeyEvent 对象 KeyEvent* keyEvent = factory->createKeyEvent(); if (!keyEvent) return NO_MEMORY; //将 从 InputChannel 中读取出来的输入事件,存放到 KeyEvent 对象中 initializeKeyEvent(keyEvent, &mMsg); *outSeq = mMsg.body.key.seq; //输入事件的唯一序列号,不为0 //outEvent 指向 keyEvent,那么此时 outEvent 就是 我们需要处理的输入事件 KeyEvent *outEvent = keyEvent; if (DEBUG_TRANSPORT_ACTIONS) { ALOGD("channel '%s' consumer ~ consumed key event, seq=%u", mChannel->getName().c_str(), *outSeq); } break; } ... } } return OK; }
- 可以看到,在
consume()
函数中,就会通过 mChannel 去从 Socket 去读取出输入事件,然后将他 封装到 KeyEvent 变量中返回
- 可以看到,在
-
至此输入事件才算真正获取到了,那么接下来就是将其进行分发,通过 JNI 调用到 JAVA 层的
dispatchInputEvent()
2. dispatchInputEvent()
android/frameworks/…/InputEventReceiver.java
private void dispatchInputEvent(int seq, InputEvent event) {
mSeqMap.put(event.getSequenceNumber(), seq);
//通过 onInputEvent() 函数调用到子类的实现中
onInputEvent(event);
}
- 在上一篇 InputChannel 的创建篇章中,我们分析过,ViewRootImpl 类中的 WindowInputEventReceiver 对象是继承 InputEventReceiver 类的,所以此时就会调用到
WindowInputEventReceiver.onInputEvent()
函数中
3. onInputEvent()
android/frameworks/…/ViewRootImpl.java
@Override
public void onInputEvent(InputEvent event) {
Trace.traceBegin(Trace.TRACE_TAG_VIEW, "processInputEventForCompatibility");
List<InputEvent> processedEvents;
try {
//做一些兼容性的处理,如果 当前版本 大于 Build.VERSION_CODES.M 则直接返回,不会处理
processedEvents =
mInputCompatProcessor.processInputEventForCompatibility(event);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
//不论是经过兼容性处理还是没有,最终都是调用 enqueueInputEvent() 函数将按键事件放入了队列中
if (processedEvents != null) {
if (processedEvents.isEmpty()) {
finishInputEvent(event, true);
} else {
for (int i = 0; i < processedEvents.size(); i++) {
enqueueInputEvent(
processedEvents.get(i), this,
QueuedInputEvent.FLAG_MODIFIED_FOR_COMPATIBILITY, true);
}
}
} else {
enqueueInputEvent(event, this, 0, true);
}
}
- 在子类 WindowInputEventReceiver 的
onInputEvent()
函数中,首先会针对当前版本做判断,针对低版本会进行相应的兼容处理,最后不论是高版本还是低版本都会调用enqueueInputEvent()
进行下一步的处理
4. enqueueInputEvent()
android/frameworks/…/ViewRootImpl.java
//入参:event, this, 0, true
@UnsupportedAppUsage
void enqueueInputEvent(InputEvent event,
InputEventReceiver receiver, int flags, boolean processImmediately) {
//将事件 event、receiver 和 flags 封装成 QueuedInputEvent 对象
QueuedInputEvent q = obtainQueuedInputEvent(event, receiver, flags);
//拿到上一个等待处理的输入事件
QueuedInputEvent last = mPendingInputEventTail;
//如果获取的上一个输入时间为空,那么就将当前的事件保存下来,表示当前没有之前剩余的需要处理的事件
//而如果 last 不为空,那么将当前的 输入事件保存到 last.mNext 中,组成链表
if (last == null) {
mPendingInputEventHead = q;
mPendingInputEventTail = q;
} else {
last.mNext = q;
mPendingInputEventTail = q;
}
//等待处理的输入事件 +1
mPendingInputEventCount += 1;
Trace.traceCounter(Trace.TRACE_TAG_INPUT, mPendingInputEventQueueLengthCounterName,
mPendingInputEventCount);
//根据入参,此时会命中if
if (processImmediately) {
doProcessInputEvents();
} else {
scheduleProcessInputEvents();
}
}
- 在 ViewRootImpl 中维护了一个待处理的输入事件的链表,mPendingInputEventHead 记录了这个链表的表头,mPendingInputEventTail 记录了这个链表的表尾,在该函数中,会先将当前输入事件进行加入链表,并将记录该链表长度的 mPendingInputEventCount 变量 加1,根据当前的入参,该事件是需要立即处理的,所以调用
doProcessInputEvents()
进行处理
5. doProcessInputEvents()
android/frameworks/…/ViewRootImpl.java
void doProcessInputEvents() {
//处理传递队列中所有待处理的输入事件
while (mPendingInputEventHead != null) {
//每次取出 mPendingInputEventHead 指向的 输入事件进行处理
QueuedInputEvent q = mPendingInputEventHead;
mPendingInputEventHead = q.mNext;
if (mPendingInputEventHead == null) {
mPendingInputEventTail = null;
}
q.mNext = null;
//此时处理了一个输入事件,所以将 count -1
mPendingInputEventCount -= 1;
Trace.traceCounter(Trace.TRACE_TAG_INPUT, mPendingInputEventQueueLengthCounterName,
mPendingInputEventCount);
long eventTime = q.mEvent.getEventTimeNano();
long oldestEventTime = eventTime;
//如果事件类型是 MotionEvent,命中if
if (q.mEvent instanceof MotionEvent) {
MotionEvent me = (MotionEvent)q.mEvent;
if (me.getHistorySize() > 0) {
oldestEventTime = me.getHistoricalEventTimeNano(0);
}
}
//更新时间
mChoreographer.mFrameInfo.updateInputEventTime(eventTime, oldestEventTime);
//开始真正消费输入事件
deliverInputEvent(q);
}
if (mProcessInputEventsScheduled) {
mProcessInputEventsScheduled = false;
mHandler.removeMessages(MSG_PROCESS_INPUT_EVENTS);
}
}
- 在该函数中,就会从 mPendingInputEventHead 开始遍历记录待处理事件的链表,将其中的输入事件逐一进行处理,
6. deliverInputEvent()
android/frameworks/…/ViewRootImpl.java
private void deliverInputEvent(QueuedInputEvent q) {
...
try {
if (mInputEventConsistencyVerifier != null) {
Trace.traceBegin(Trace.TRACE_TAG_VIEW, "verifyEventConsistency");
try {
mInputEventConsistencyVerifier.onInputEvent(q.mEvent, 0);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
}
InputStage stage;
if (q.shouldSendToSynthesizer()) {
stage = mSyntheticInputStage;
} else {
//shouldSkipIme()判断是否不需要将其传递给输入法,只有在设置 FLAG_DELIVER_POST_IME 后才为 false
//简单来说,就是判断当前是否存在输入法窗口,如果存在,那么为mFirstInputStage,否则就是mFirstPostImeInputStage
stage = q.shouldSkipIme() ? mFirstPostImeInputStage : mFirstInputStage;
}
if (q.mEvent instanceof KeyEvent) {
Trace.traceBegin(Trace.TRACE_TAG_VIEW, "preDispatchToUnhandledKeyManager");
try {
//如果当前是按键事件,那么会先清除 up 事件
mUnhandledKeyManager.preDispatch((KeyEvent) q.mEvent);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
}
if (stage != null) {
handleWindowFocusChanged();
//假设当前没有输入法窗口,调用 deliver() 函数从 mFirstPostImeInputStage 开始遍历找到处理该输入事件的 对象
stage.deliver(q);
} else {
finishInputEvent(q);
}
} finally {
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
}
- 在这里就开始将输入事件发送给真正进行处理它的对象了,在该函数中有一个非常关键的步骤,就是决定调用链的起始对象,在这里我们需要先解释一下 Input 处理过程中的 责任链模式。
责任链:
-
在 Android 中,因为窗口类型的不同,输入事件类型的不同,那么处理该输入事件的方式也不同,所以就有了责任链模式,用来确定对应的输入事件由谁来进行处理,在创建了一个窗口之后,
setView()
的过程中,会创建那么几个对象public void SetView() { ... mSyntheticInputStage = new SyntheticInputStage(); InputStage viewPostImeStage = new ViewPostImeInputStage(mSyntheticInputStage); InputStage nativePostImeStage = new NativePostImeInputStage(viewPostImeStage, "aq:native-post-ime:" + counterSuffix); InputStage earlyPostImeStage = new EarlyPostImeInputStage(nativePostImeStage); InputStage imeStage = new ImeInputStage(earlyPostImeStage, "aq:ime:" + counterSuffix); InputStage viewPreImeStage = new ViewPreImeInputStage(imeStage); InputStage nativePreImeStage = new NativePreImeInputStage(viewPreImeStage, "aq:native-pre-ime:" + counterSuffix); mFirstInputStage = nativePreImeStage; mFirstPostImeInputStage = earlyPostImeStage; }
-
在 ViewRootImpl 的
SetView()
函数中,会创建对应的 Stage 对象,这几个 Stage 对象的继承关系如下图所示:而根据创建的方式,我们又可以得到这几个对象之间的关系:
如图所示,除了 SyntheticInputStage 之外,其他的几个对象,每个其中都存在一个
InputStage mNext
成员变量,存放的就是存放的就是另一个对象,关联关系就如上图,至此,七个对象就关联了起来,后续就可以通过拿到 NativePreImeInputStage 对象逐一拿到其余的所有对象,这就是 Input 处理流程中的 责任链 -
那么接下来,就让我们简单过一下这个调用流程
-
7. deliver()
android/frameworks/…/ViewRootImpl.java
/**
* Delivers an event to be processed.
*/
public final void deliver(QueuedInputEvent q) {
if ((q.mFlags & QueuedInputEvent.FLAG_FINISHED) != 0) {
forward(q);
} else if (shouldDropInputEvent(q)) {
finish(q, false);
} else {
traceEvent(q, Trace.TRACE_TAG_VIEW);
final int result;
try {
//这里时候如果不存在输入法窗口,那么就是调用到 EarlypostImeInputStage 对象的 onProcess()
result = onProcess(q);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
apply(q, result);
}
}
-
deliver()
函数只有父类 InputStage 对象中有实现,其他的子类都没有进行覆写,由上面的分析可知,此时子类是 EarlyPostImeInputStage 对象,所以此时调用的就是 子类 EarlyPostImeInputStage 的onProcess()
函数
ⅰ onProcess()
android/frameworks/…/ViewRootImpl.java
final class EarlyPostImeInputStage extends InputStage {
...
@Override
protected int onProcess(QueuedInputEvent q) {
//假定此时是键盘事件,那么会命中该if
if (q.mEvent instanceof KeyEvent) {
//返回的是 FORWARD
return processKeyEvent(q);
} else if (q.mEvent instanceof MotionEvent) {
return processMotionEvent(q);
}
return FORWARD;
}
....
private int processKeyEvent(QueuedInputEvent q) {
final KeyEvent event = (KeyEvent)q.mEvent;
if (mAttachInfo.mTooltipHost != null) {
mAttachInfo.mTooltipHost.handleTooltipKey(event);
}
if (checkForLeavingTouchModeAndConsume(event)) {
return FINISH_HANDLED;
}
//分发给 AudioManger 先去处理
mFallbackEventHandler.preDispatchKeyEvent(event);
//返回 FORWARD ,继续向上查找处理者
return FORWARD;
}
...
}
- 可以看到在经过 EarlyPostImeInputStage 中的
onProcess()
函数处理后,此时返回的是 FORWARD,那么接下来会继续调用apply()
去进行处理
ⅱ apply()
- 需要说明的是,
apply()
函数只有两个基类 InputStage 和 AsyncInputStage 有实现,但是仔细梳理可以发现 AsyncInputStage 只是做了一层封装,增加了一个提前消耗输入事件的过程,除却前面的场景,还是会调用到 InputStage 去处理 - 此处因为 EarlyPostImeInputStage 继承的是 InputStage ,所以这里会直接调用 InputStage 的
apply()
函数
protected void apply(QueuedInputEvent q, int result) {
if (result == FORWARD) {
//此时命中该if,继续向上调用
forward(q);
} else if (result == FINISH_HANDLED) {
finish(q, true);
} else if (result == FINISH_NOT_HANDLED) {
finish(q, false);
} else {
throw new IllegalArgumentException("Invalid result: " + result);
}
}
-
forward()
函数和apply()
一样,此处也是直接调用到 InputStage 的forward()
函数
protected void forward(QueuedInputEvent q) {
//此处调用的就是 InputStage 的 onDeliverToNext()
onDeliverToNext(q);
}
protected void onDeliverToNext(QueuedInputEvent q) {
if (DEBUG_INPUT_STAGES) {
Log.v(mTag, "Done with " + getClass().getSimpleName() + ". " + q);
}
if (mNext != null) {
//在这里调用 mNext 的 deliver()
mNext.deliver(q);
} else {
finishInputEvent(q);
}
}
-
最终调用到
mNext.deliver()
函数,那么 这个 mNext 变量又是从何而来的呢?此时真正的对象是 EarlyPostImeInputStage ,我们来看一下它的构造函数:public EarlyPostImeInputStage(InputStage next) { //调用了基类的构造函数,而EarlyPostImeInputStage的基类就是InputStage super(next); } public (InputStage next) { //也就是说构造传入的参数,最终是记录在 mNext 中的 mNext = next; }
而根据之前的分析,
InputStage earlyPostImeStage = new EarlyPostImeInputStage(nativePostImeStage);
可以看到,在 EarlyPostImeInputStage 对象中 mNext 变量指向的其实就是 NativePostImeInputStage 对象,所以最终调用到的就是NativePostImeInputStage.deliver()
,而依据上面的分析,deliver()
有会调用具体子类的onProcess()
函数中,所以就这样一层一层就向上传递,直到找到可以处理该输入事件的相应对象为止。
总结:
- 经过上面的分析,我们可以总结一下对应的应用程序窗口接收到一个输入事件需要执行的对应流程:
- 当 InputDispatcher 线程向 Server 端的 InputChannel 中的 Socket 中写入了一个输入事件后,对应应用程序窗口的 Looper 循环就会被唤醒,唤醒后就会调用
handleEvent()
函数进行处理 - 在
handleEvent()
函数中,首先会通过 client 端的 InputChannel 中的 Socket 获取到对应的输入事件 - 获取到输入事件后,通过 JNI 将该输入事件交给 InputEventReceiver 对象的
dispatchInputEvent()
函数进行处理 - 而在 InputEventReceiver 对象中,则会根据责任链的方式,去寻找真正处理该输入事件的对象,然后交由其去处理该输入事件
- 当 InputDispatcher 线程向 Server 端的 InputChannel 中的 Socket 中写入了一个输入事件后,对应应用程序窗口的 Looper 循环就会被唤醒,唤醒后就会调用