事件分发的整体流程
ViewRootImpl 及其后面是客户端的时序,本文主要聚焦客户端中事件分发的流程
ViewRootImpl 分发事件
dispatchInputEvent(InputEvent event)
是 ViewRootImpl 分发事件的起点,event 是输入事件
ViewRootImpl
收到输入事件后,会使用enqueueInputEvent()
将其加入到消息队列,obtainQueuedInputEvent()
将InputEvent
转为队列所需的QueuedInputEvent
数据结构- 消息队列的消息都会在
doProcessInputEvents()
中被交由deliverInputEvent
方法处理 deliverInputEvent()
方法借助InputStage
链表结构,采用责任链模式对事件进行遍历、传递
在android.view.ViewRootImpl#deliverInputEvent
中,相关的代码实现如下
InputStage stage;
if (q.shouldSendToSynthesizer()) {
stage = mSyntheticInputStage;
} else {
stage = q.shouldSkipIme() ? mFirstPostImeInputStage : mFirstInputStage;
}
if (q.mEvent instanceof KeyEvent) {
Trace.traceBegin(Trace.TRACE_TAG_VIEW, "preDispatchToUnhandledKeyManager");
try {
mUnhandledKeyManager.preDispatch((KeyEvent) q.mEvent);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
}
if (stage != null) {
handleWindowFocusChanged();
stage.deliver(q);
} else {
finishInputEvent(q);
}
其中InputStage
在 ViewRootImpl.java#setView(View,WindowManager.LayoutParams,View,int)
中被初始化
// Set up the input pipeline.
CharSequence counterSuffix = attrs.getTitle();
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;
SyntheticInputStage
、ViewPostImeInputStage
等都继承自 InputStage
,并重写了下面这个构造方法
public InputStage(InputStage next) {
mNext = next;
}
可见,不同的InputStage
构建了一个链表结构,其头节点被 mFirstInputStage
所引用,每个节点包含 next 指针,指向下一个 InputStage
这里对各个InputStage进行简要说明:
InputStage | 说明 |
---|---|
NativePreImeInputStage | 分发早于IME的InputEvent到NativeActivity中去处理, NativeActivity和普通acitivty的功能一致,不过是在native层实现,这样执行效率会更高,同时NativeActivity在游戏开发中很实用(不支持触摸事件)。 |
ViewPreIMEInputStage | 分发早于IME的InputEvent到View框架处理,会调用view(输入焦点)的onkeyPreIme方法,同时会给View在输入法处理key事件之前先得到消息并优先处理,View系列控件可以直接复写onKeyPreIme( 不支持触摸事件)。 |
ImeInputStage | 分发InputEvent到IME处理调用ImeInputStage的onProcess,InputMethodManager的dispatchInputEvent方法处理消息(不支持触摸事件)。 |
EarlyPostImeInputStage | 与touchmode相关,比如你的手机有方向键,按方向键会退出touchmode,这个事件被消费,有可能会有view的背景变化,但不确定(支持触摸事件)。 |
NativePostImeInputStage | 分发InputEvent事件到NativeActivity,IME处理完消息后能先于普通Activity处理消息(此时支持触摸事件)。 |
ViewPostImeInputStage | 分发InputEvent事件到View框架,view的事件分发(支持触摸事件)。最终会调用到输入焦点的3个方法:使用setKeyListener注册的监听器的onKey,之后是onKeyDown和onKeyUp,或者调用activity的onKeyDown和onKeyUp方法,也就是兜底处理无人处理的key事件。 |
SyntheticInputStage | 未处理 InputEvent最后处理。 |
在android.view.ViewRootImpl#deliverInputEvent
中,如果 stage
不为 null 则会执行 android.view.ViewRootImpl.InputStage#deliver
方法
if (stage != null) {
handleWindowFocusChanged();
stage.deliver(q);
} else {
finishInputEvent(q);
}
android.view.ViewRootImpl.InputStage#deliver
方法实现如下
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 {
result = onProcess(q);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
apply(q, result);
}
}
事件依次经过每个Stage
,如果该事件没有被标识为 “Finished”, 该Stage
就会处理它,然后返回处理结果Forward
或Finish
,Forward
运行下一个Stage
继续处理,而Finished
将会给该事件设置上“Finished”标识,并向下一个 Stage
前进,直到最后一级 SyntheticInputStage
。
Stage
调用 onProcess
处理它的事件,这里采用模板模式,onProcess
是一个钩子方法,不同子类的实现不同,对于触屏事件,和按键事件来说,主要是由 ViewPostImeInputStage
来处理。
ViewPostImeInputStage
android.view.ViewRootImpl.ViewPostImeInputStage#onProcess
@Override
protected int onProcess(QueuedInputEvent q) {
if (q.mEvent instanceof KeyEvent) {
return processKeyEvent(q);
} else {
final int source = q.mEvent.getSource();
if ((source & InputDevice.SOURCE_CLASS_POINTER) != 0) {
return processPointerEvent(q);
} else if ((source & InputDevice.SOURCE_CLASS_TRACKBALL) != 0) {
return processTrackballEvent(q);
} else {
return processGenericMotionEvent(q);
}
}
}
onProcess 方法根据不同的事件类型进行划分,触屏事件走的是 processPointerEvent()
, 按键事件走的是 processKeyEvent()
以触屏事件为例,
android.view.ViewRootImpl.ViewPostImeInputStage#processPointerEvent
private int processPointerEvent(QueuedInputEvent q) {
final MotionEvent event = (MotionEvent)q.mEvent;
mAttachInfo.mUnbufferedDispatchRequested = false;
mAttachInfo.mHandlingPointerEvent = true;
boolean handled = mView.dispatchPointerEvent(event);
maybeUpdatePointerIcon(event);
maybeUpdateTooltip(event);
mAttachInfo.mHandlingPointerEvent = false;
if (mAttachInfo.mUnbufferedDispatchRequested && !mUnbufferedInputDispatch) {
mUnbufferedInputDispatch = true;
if (mConsumeBatchedInputScheduled) {
scheduleConsumeBatchedInputImmediately();
}
}
return handled ? FINISH_HANDLED : FORWARD;
}
其中 boolean handled = mView.dispatchPointerEvent(event);
处将事件转交给 ViewRootImpl
下的 View 去处理
android.view.View#dispatchPointerEvent
@UnsupportedAppUsage
public final boolean dispatchPointerEvent(MotionEvent event) {
if (event.isTouchEvent()) {
return dispatchTouchEvent(event);
} else {
return dispatchGenericMotionEvent(event);
}
}
dispatchPointerEvent()
中判断,如果是触摸事件,则走 dispatchTouchEvent()
, 否则执行 dispatchGenericMotionEvent()
上述过程的时序图为
参考博文
https://www.cnblogs.com/huan89/p/14286463.html
http://gityuan.com/2016/12/31/input-ipc/
https://blog.csdn.net/vviccc/article/details/93377708