在之前的文章中,我分析了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