在之前的文章中,我分析了Input点击事件从底层到JAVA层的传输过程,在分析到事件传输到InputEventReceiver时便没有继续分析。这一篇文章将会从onInputEvent方法为起点,系统性的分析Android对触摸事件是如何派发给控件树的。
文章的开头照旧给出整体类图、时序图方便掌握整体与回忆。之后会详细探讨InputStage的责任链模式涉及、dispatchTouchEvent派发过程。文章的最后会给出一张事件派发流转图作整体回顾。
设计模式中的责任链模式充分发挥了OOP三大特性"封装"、"继承"、"多态",巧妙的使用了继承与关联的对象关系,将类的单一职责进行封装并将各个职责对象进行关联,实现了一个从头到尾的职责链,链上的节点如果不能够处理任务将会交给下一个节点进行处理。
frameworks/base/core/java/android/view/ViewRootImpl#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;
这一责任链又可以被形象的称为输入事件处理的 pipeline,Android 是在APP要显示视图的过程中将它进行初始化的,关于ViewRootImpl相关的视图初始化、绘制相关分析,可以看我之前的文章。该代码总结后可以得到下面的图:
我们这里跟踪的触摸流程将会途径EarlyPostImeInputStage->NativePostImeInputStage->ViewPostImeInputStage三条流水线,其中ViewPostImeInputStage的onProcess将会处理按键、触摸事件。
特意用AS调试抓了一张input事件处理的调用栈,可以看到责任链模式刚好经过两个deliver->apply->forward->onDliverToNext的阶段,并且最终进入了ViewPostImeInputStage的onProcess方法,开始对View的事件进行派发。
这里总结一下 InputStage 责任链核心方法的作用:
| 方法名 | 作用 |
|---|---|
| deliver | 首先会根据事件状态来选择是否需要直接跳过与结束。一般调用apply进行事件处理 |
| onProcess | 进行事件的处理,根据处理状态返回状态值 |
| apply | 根据onProcess返回的状态值来决定是继续处理事件还是结束处理,如果事件已经被处理,那么将会进行结束阶段 |
| forward | 当前Stage不能处理该事件,将会调用onDeliverToNext处理 |
| onDeliverToNext | 调用下一个Stage进行事件处理 |
| finish | 结束事件处理,调用此方法后,之后的阶段将会在deliver时直接调用forward |
input stage 事件处理pipeline各个stage的作用我就不分析了,这边直接入正题,触摸相关的事件会在ViewPostImeInputStage中进行处理
frameworks/base/core/java/android/view/ViewRootImpl.java
@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); // 处理触摸事件
}
...
}
}
private int processPointerEvent(QueuedInputEvent q) {
final MotionEvent event = (MotionEvent)q.mEvent;
mAttachInfo.mUnbufferedDispatchRequested = false;
// 获取到事件传递对象 mView
final View eventTarget =
(event.isFromSource(InputDevice.SOURCE_MOUSE) && mCapturingView != null) ?
mCapturingView : mView;
mAttachInfo.mHandlingPointerEvent = true;
// 调用方法进行事件派发
boolean handled = eventTarget.dispatchPointerEvent(event);
...
// 如果事件被处理,则结束input stage pipeline,如果否,则继续派发到下一stage
return handled ? FINISH_HANDLED : FORWARD;
}
dispatchPointerEvent为整个视图树派发的入口,其中的View是调用addView时传给ViewRootImpl的。这块的流程我在之前的Activity视图显示分析中提到过,mView实际上是DecorView的实例
frameworks/base/core/java/android/view/View.java
public final boolean dispatchPointerEvent(MotionEvent event) {
if (event.isTouchEvent()) {
return dispatchTouchEvent(event); // 随后会分发到ViewGroup或View的对应方法
} else {
return dispatchGenericMotionEvent(event);
}
}
DecorView并没有覆写dispatchPointerEvent方法,直接调用的父类View的实现。这里随后会调用DecorView的dispatchTouchEvent方法中
frameworks/base/core/java/com/android/internal/policy/DecorView.java
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
final Window.Callback cb = mWindow.getCallback();
return cb != null && !mWindow.isDestroyed() && mFeatureId < 0
? cb.dispatchTouchEvent(ev) : super.dispatchTouchEvent(ev);
}
这里的Callback实例实际上是由Activity进行实现的,也就是说DecorView的该方法会随后调用Activity的dispatch方法
frameworks/base/core/java/android/app/Activity.java
public boolean dispatchTouchEvent(MotionEvent ev) {
...
// 处理Activity相关的逻辑后,直接调用PhoneWindow的方法进行事件派发
if (getWindow().superDispatchTouchEvent(ev)) {
return true;
}
// 如果事件没有被处理,则调用onTouchEvent方法
return onTouchEvent(ev);
}
这里需要注意的是:
frameworks/base/core/java/com/android/internal/policy/PhoneWindow.java
@Override
public boolean superDispatchTouchEvent(MotionEvent event) {
// 又重新调用了DecorView的派发方法
return mDecor.superDispatchTouchEvent(event);
}
因为DecorView是ViewGroup的子类,所以接下来就到了事件派发的核心方法dispatchTouchEvent
```java
@Override