事件分发的整体流程
屏幕硬件
Linux 驱动
InputManagerService
ViewRootImpl
Activity
Activity包含的子View
接收中断
1
获取触摸事件
2
分发事件
3
分发事件
4
处理事件
5
响应事件
6
响应事件
7
事件处理完成
8
重绘屏幕
9
重绘屏幕
10
屏幕硬件
Linux 驱动
InputManagerService
ViewRootImpl
Activity
Activity包含的子View
ViewRootImpl 及其后面是客户端的时序,本文主要聚焦客户端中事件分发的流程
ViewRootImpl 分发事件
dispatchInputEvent(InputEvent event) 是 ViewRootImpl 分发事件的起点,event 是输入事件
服务端
ViewRootImpl
InputStage
dispatchInputEvent(InputEvent event)
1
dispatchInputEvent(event, null)
2
Handler 消息
3
enqueueInputEvent(event, receiver, 0, true)
4
obtainQueuedInputEvent(event, receiver, flags)
5
doProcessInputEvents()
6
deliverInputEvent(QueuedInputEvent q)
7
deliver(QueuedInputEvent q))
8
服务端
ViewRootImpl
InputStage
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) 中被初始化
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()
上述过程的时序图为
InputStage
ViewPostImeInputStage
View
onProcess(QueuedInputEvent q)
1
processPointerEvent(QueuedInputEvent q)
2
dispatchPointerEvent(MotionEvent event)
3
dispatchTouchEvent(MotionEvent event)
4
InputStage
ViewPostImeInputStage
View
参考博文
https://www.cnblogs.com/huan89/p/14286463.html
http://gityuan.com/2016/12/31/input-ipc/
https://blog.csdn.net/vviccc/article/details/93377708
|