前一篇 Android Touch Event 分发和处理中介绍了 TouchEvent 是怎样在 Activity, ViewGroup 和 View 之间分发和处理的。再深入一点看 TouchEvent 事件是怎样从系统来到 Activity 的。
下面的图很清晰地展示了 TouchEvent 是怎样在 Activity, ViewGroup 和 View 之间分发和处理的。

一个很自然的疑问是 TouchEvent 事件是怎样从系统来到 Activity 的。
Activity TouchEvent 事件
1 2 3 4
| flowchart native_code-->|dispatchInputEvent|InputEventReceiver InputEventReceiver-->|onInputEvent|WindowInputEventReceiver WindowInputEventReceiver-->|enqueueInputEvent|ViewRootImpl
|
整理了一下主要流程:

这里附上最关键的两个代码,方便大家查看。
之后执行 ViewRootImpl.deliverInputEvent()
方法,进入 ViewRootImpl 内部的 Input Pipeline。

Input Pipeline 是一条 InputStage 链(责任链模式),可以粗略分成三个阶段:
- preIme stage
- ime stage
- PostIme stage
重点看 ViewPostImeInputStage
。看注释容易理解这个类的作用是将 Input Event 分发到 View 树。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
|
final class ViewPostImeInputStage extends InputStage { public ViewPostImeInputStage(InputStage next) { super(next); }
@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); } } }
private int processGenericMotionEvent(QueuedInputEvent q) { final MotionEvent event = (MotionEvent)q.mEvent;
if (event.isFromSource(InputDevice.SOURCE_TOUCHPAD)) { if (hasPointerCapture() && mView.dispatchCapturedPointerEvent(event)) { return FINISH_HANDLED; } }
if (mView.dispatchGenericMotionEvent(event)) { return FINISH_HANDLED; } return FORWARD; } }
|
最关键的地方在第35行,这里的 mView
并不是一个普通的 View,它正是 Window.mDecor
从 DecorView 到 Activity
这个流程比较简单,就直接上代码了。
DecorView.dispatchGenericMotionEvent
代码如下:
1 2 3 4 5 6
| @Override public boolean dispatchGenericMotionEvent(MotionEvent ev) { final Window.Callback cb = mWindow.getCallback(); return cb != null && !mWindow.isDestroyed() && mFeatureId < 0 ? cb.dispatchGenericMotionEvent(ev) : super.dispatchGenericMotionEvent(ev); }
|
以上第三行的 cb
正是 Activity,因为 Activity 创建对应的 Window 对象时,将自身作为 Window.Callback 参数,见以下第9行。(注:cb
也可能是 Dialog 或其他对象,简单起见我们暂且认为肯定是 Activity)
1 2 3 4 5 6 7 8 9 10 11 12 13
| public class Activity extends ContextThemeWrapper implements LayoutInflater.Factory2, Window.Callback, KeyEvent.Callback, @UnsupportedAppUsage private Window mWindow; final void attach(Context context, ActivityThread aThread, ...) { ... mWindow = new PhoneWindow(this, window, activityConfigCallback); ... } }
|
我们在 Activity.dispatchTouchEvent()
方法里面打个断点,对比方法调用栈很容易验证以上代码流程。

相关代码
前一节分析了 TouchEvent 事件是怎样从系统中的 InputEventReceiver 来到 Activity 的。这有点像观察水管里的水是如何流动的。
那水管是如何修起来的呢?我们可以看 Activity 以及其关联 Window 的创建过程。但我发现这有些不必要地让事情变得复杂。
其实我们完全可以把这里的 Activity 换 Dialog。因为从窗口管理和事件分发来看,Dialog 跟 Activity 没有本质的不同,但代码要简单得多。
几句话就能描述清楚:
- Dialog 创建 Window
- Window 创建 DecorView
- Dialog 请求
WindowManager.addView()
添加 DecorView
- 调用 WindowManagerImpl 和WindowManagerGlobal 对应方法
WindowManagerGlobal.addView()
创建 ViewRootImpl
- 让 ViewRootImpl 持有 DecorView 的引用
- ViewRootImpl 为 DecorView 创建 WindowInputEventReceiver (以及对应的 InputChannel)
Dialog.java - Android Code Search
- Dialog 创建 Window
- Window 创建 DecorView
- Dialog 请求
WindowManager.addView()
添加 DecorView
特别注意这里的第18行 Window 将当前的 Dialog 设置为 callback。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| Dialog(@UiContext @NonNull Context context, @StyleRes int themeResId, boolean createContextThemeWrapper) { if (createContextThemeWrapper) { if (themeResId == Resources.ID_NULL) { final TypedValue outValue = new TypedValue(); context.getTheme().resolveAttribute(R.attr.dialogTheme, outValue, true); themeResId = outValue.resourceId; } mContext = new ContextThemeWrapper(context, themeResId); } else { mContext = context; }
mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
final Window w = new PhoneWindow(mContext); mWindow = w; w.setCallback(this); } public void show() { ... mDecor = mWindow.getDecorView(); ... mWindowManager.addView(mDecor, l); ... }
|
从 WindowManager.addView() 到 WindowManagerGlobal.addView() 有一些跳跃性。需要了解一些背景知识,即这几个类之间的关系以及 Window 关联的 WindowManager 实例是如何创建的。

WindowManagerGlobal.java - Android Code Search
WindowManagerGlobal.addView()
创建 ViewRootImpl
- 让 ViewRootImpl 持有 DecorView 的引用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| public void addView(View view, ViewGroup.LayoutParams params, Display display, Window parentWindow, int userId) { ... if (windowlessSession == null) { root = new ViewRootImpl(view.getContext(), display); } else { root = new ViewRootImpl(view.getContext(), display, windowlessSession, new WindowlessWindowLayout()); } ... try { root.setView(view, wparams, panelParentView, userId); } catch (RuntimeException e) { final int viewIndex = (index >= 0) ? index : (mViews.size() - 1); if (viewIndex >= 0) { removeViewLocked(viewIndex, true); } throw e; } } }
|
ViewRootImpl.java - Android Code Search
- ViewRootImpl 为 DecorView 创建 WindowInputEventReceiver (以及对应的 InputChannel)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
|
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView, int userId) { ... InputChannel inputChannel = null; if ((mWindowAttributes.inputFeatures & WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) { inputChannel = new InputChannel(); }
mWindowSession.addToDisplayAsUser(mWindow, mWindowAttributes, getHostVisibility(), mDisplay.getDisplayId(), userId, mInsetsController.getRequestedVisibleTypes(), inputChannel, mTempInsets, mTempControls, attachedFrame, compatScale);
if (inputChannel != null) { if (mInputQueueCallback != null) { mInputQueue = new InputQueue(); mInputQueueCallback.onInputQueueCreated(mInputQueue); } mInputEventReceiver = new WindowInputEventReceiver(inputChannel, Looper.myLooper()); } ... }
|
到这里,水管就建好了。水可以流动了,也就是说:
- Native 代码可以通过 InputChannel 向 WindowInputEventReceiver 发送 Input 事件
- 这些事件经过 ViewRootImpl 和 DecorView,最终会来到 Dialog 或 Activity (它们都是 Window.Callback)
参考