目录
1 Android native层传递点击事件
2 .Window和WindowManagerService的关系
2.1 ViewRootImpl 如何串联Window和WindowManagerService
2.2 ViewRootImpl和Window的关系
2.3 ViewRootImpl和WindowManagerService的关系
以上参考前一个章节
[3 ViewRootImpl的事件接收及分发](#3 ViewRootImpl的事件接收及分发)
以下章节待分解:
- ViewGroup事件的分发机制
4.1 activity的视图创建流程
4.2 ViewGroup的事件分发流程
4.3 View的事件处理
- 事件分发具体案例及解决方案

图1 Android屏幕触控机制整体流程图
3 ViewRootImpl的事件接收及分发

图2
ViewRootImpl类中有一个类 WindowInputEventReceiver
WindowInputEventReceiver继承自InputEventReceiver
这边InputDispatcher分发到java侧的点击事件就会被InputEventReceiver.onInputEvent的接收到
当一个触摸事件传递后来后, WindowInputEventReceiver收到触摸事件,
会调用到onInputEvent方法,进而执行enqueueInputEvent方法
java
final class WindowInputEventReceiver extends InputEventReceiver {
public WindowInputEventReceiver(InputChannel inputChannel, Looper looper) {
super(inputChannel, looper);
}
@Override
public void onInputEvent(InputEvent event, int displayId) {
enqueueInputEvent(event, this, 0, true);
}
...
}
java
void enqueueInputEvent(InputEvent event,
InputEventReceiver receiver, int flags, boolean processImmediately) {
adjustInputEventForCompatibility(event);
//将当前输入事件加入队列中排列等候执行
QueuedInputEvent q = obtainQueuedInputEvent(event, receiver, flags);
//输入事件添加进队列后,加入输入事件的默认尾部
QueuedInputEvent last = 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(); // 发送handler消息,最终还是会调用 doProcessInputEvents方法处理消息
}
}
doProcessInputEvents 循环处理所有的可处理的输入事件
java
void doProcessInputEvents() {
//循环处理完所有可处理的输入事件
while (mPendingInputEventHead != null) {
......
//分发处理
deliverInputEvent(q);
}
// 循环处理完所有可处理的输入事件,清除延迟处理标志位
if (mProcessInputEventsScheduled) {
mProcessInputEventsScheduled = false;
mHandler.removeMessages(MSG_PROCESS_INPUT_EVENTS);
}
}
可以看到,在处理事件的deliverInputEvent()方法里有一个InputStage类,这个类是在ViewRootImpl的setView()里创建的,这里会执行stage.deliver(q)方法,默认开始从链表最上端的NativePreImeInputStage开始处理,
java
private void deliverInputEvent(QueuedInputEvent q) {
Trace.asyncTraceBegin(Trace.TRACE_TAG_VIEW, "deliverInputEvent",
q.mEvent.getSequenceNumber());
...
InputStage stage;
if (q.shouldSendToSynthesizer()) { // 默认为false 综合处理事件阶段,比如处理导航面板、操作杆等事件。
stage = mSyntheticInputStage;
} else {
// 是否跳过输入法窗口进行分发 shouldSkipIme() 默认为false
stage = q.shouldSkipIme() ? mFirstPostImeInputStage : mFirstInputStage;
}
...
if (stage != null) {
handleWindowFocusChanged();
stage.deliver(q);
} else {
finishInputEvent(q);
}
}
InputStage类中根据链表形式将输入的事件的责任链拼接完成了,不同的InputStage子类,通过构造方法依次串联了起来,每个子类代表不同的事件
SyntheticInputStage。综合处理事件阶段,比如处理导航面板、操作杆等事件。
ViewPostImeInputStage。视图输入处理阶段,比如按键、手指触摸等运动事件,我们熟知的view事件分发就发生在这个阶段。
NativePostImeInputStage。 分发InputEvent事件到NativeActivity,为了让IME处理完消息后能先于普通的Activity处理消息
EarlyPostImeInputStage。输入法早期处理阶段。
ImeInputStage。输入法事件处理阶段,处理输入法字符。
ViewPreImeInputStage。视图预处理输入法事件阶段,调用视图view的dispatchKeyEventPreIme方法。
NativePreImeInputStage。本地方法预处理输入法事件阶段。
java
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
synchronized (this) {
......
// 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;
}
deliver的入口会判断当前QueuedInputEvent的状态。
如果判断QueuedInputEvent打开FLAG_FINISHED标志位,换句话说就是不是通过finish方法进来的,就会执行forward的方法。
如果判断到当前Window失去焦点,或者还没有进行刷新ui,QueuedInputEvent则执行finish
剩下的情况执行apply的默认方法,而执行的方法由每一个InputStage的子类复写onProcess标志位决定的。
因为咱们主要关心的是用户的触屏点击等操作,所以主要关注的类的ViewPostImeInputStage()
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 {
apply(q, onProcess(q));
}
}
在ViewPostImeInputStage()方法中, processKeyEvent方法为处理keyEvent(按键)事件
如果是手指的触摸事件,比如按下滑动,多手势等,就会走到processPointerEvent()方法中
java
@Override
protected int onProcess(QueuedInputEvent q) {
if (q.mEvent instanceof KeyEvent) {
return processKeyEvent(q); // keyEvent(按键)事件
} 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); // 一般的motion事件
}
}
}
在processPointerEvent()方法中,最终会调用mView.dispatchPointerEvent(event)方法来做按键的处理,之后就涉及到android的事件分发流程
java
private int processPointerEvent(QueuedInputEvent q) {
final MotionEvent event = (MotionEvent)q.mEvent;
...
mAttachInfo.mUnbufferedDispatchRequested = false;
mAttachInfo.mHandlingPointerEvent = true;
boolean handled = mView.dispatchPointerEvent(event); //传递给view 的dispatchPointerEvent方法处理 最终会做事件分发处理
maybeUpdatePointerIcon(event);
maybeUpdateTooltip(event);
mAttachInfo.mHandlingPointerEvent = false;
if (mAttachInfo.mUnbufferedDispatchRequested && !mUnbufferedInputDispatch) {
mUnbufferedInputDispatch = true;
if (mConsumeBatchedInputScheduled) {
scheduleConsumeBatchedInputImmediately();
}
}
return handled ? FINISH_HANDLED : FORWARD;
}