要理解直接通过WindowManager
添加的 View 的事件传递机制,以及它与 Activity 中 View 的差异,我们需要从事件传递的起点 、Window 与 ViewRootImpl 的关联 、根 View 的事件分发逻辑三个层面结合源码分析。
一、事件传递的总入口:InputManagerService 与 ViewRootImpl
无论哪种 View(Activity 中的 View 或 WindowManager 添加的 View),触摸事件的源头都是InputManagerService(IMS) 。IMS 通过系统底层捕获触摸事件后,会根据事件坐标和 Window 的 Z-order(层级)确定目标 Window,然后通过该 Window 对应的InputChannel 将事件传递给其关联的ViewRootImpl
。
ViewRootImpl
是事件传递到 View 树的第一个 Java 层节点,其核心处理逻辑在dispatchInputEvent(InputEvent event)
方法中:
java
// ViewRootImpl.java
public void dispatchInputEvent(InputEvent event, InputEventReceiver receiver) {
// 1. 创建事件处理的Runnable
InputStage stage = mFirstInputStage;
if (stage != null) {
stage.deliver(event, receiver); // 事件进入处理流水线
} else {
// 若无处理阶段,直接finish
finishInputEvent(event, receiver, true);
}
}
InputStage
是事件处理的流水线,最终会调用到ViewPostImeInputStage
的onProcess()
方法,在这里完成从ViewRootImpl
到根 View 的事件传递:
java
// ViewRootImpl$ViewPostImeInputStage.java
protected int onProcess(InputEvent event) {
if (event instanceof MotionEvent) {
// 核心:将触摸事件分发给根View(mView)
return processPointerEvent((MotionEvent) event);
}
// ...其他事件类型
}
private int processPointerEvent(MotionEvent event) {
// mView是当前Window的根View(如Activity的DecorView,或WindowManager添加的View)
if (mView.dispatchPointerEvent(event)) {
return FINISH_HANDLED; // 事件被消费
}
return FORWARD; // 未被消费,继续传递
}
可见:所有 Window 的事件都会先经过ViewRootImpl
,再传递给其管理的根 View(mView) 。这是两种场景的共同点。
二、Activity 中 View 的事件传递路径:多了 Activity 的 "中转"
Activity 中的 View(如布局中的 Button)的根 View 是DecorView
,而DecorView
与 Activity 存在强关联(Activity 是PhoneWindow
的Callback
),这导致事件传递多了一层 "Activity 中转"。
具体路径拆解(结合源码):
-
ViewRootImpl → DecorView :
ViewRootImpl
的mView
是DecorView
,调用DecorView.dispatchPointerEvent(event)
,最终会触发DecorView.dispatchTouchEvent(event)
。 -
DecorView → Activity :
DecorView
的dispatchTouchEvent
会优先将事件交给其关联的Callback
(即 Activity):java// DecorView.java @Override public boolean dispatchTouchEvent(MotionEvent ev) { final Window.Callback cb = mWindow.getCallback(); // mWindow是PhoneWindow,其Callback是Activity return cb != null && !mWindow.isDestroyed() ? cb.dispatchTouchEvent(ev) // 调用Activity的dispatchTouchEvent : super.dispatchTouchEvent(ev); }
-
Activity → PhoneWindow → DecorView :Activity 的
dispatchTouchEvent
会将事件回传给PhoneWindow
,最终回到DecorView
:java// Activity.java public boolean dispatchTouchEvent(MotionEvent ev) { if (ev.getAction() == MotionEvent.ACTION_DOWN) { onUserInteraction(); // 空实现,供子类重写 } // getWindow()是PhoneWindow,调用其superDispatchTouchEvent if (getWindow().superDispatchTouchEvent(ev)) { return true; // 事件被消费 } return onTouchEvent(ev); // 若所有View都不消费,Activity自己处理 } // PhoneWindow.java public boolean superDispatchTouchEvent(MotionEvent event) { // 最终调用DecorView的super.dispatchTouchEvent(即ViewGroup的实现) return mDecor.superDispatchTouchEvent(event); }
-
DecorView → View 树 :回到
DecorView
后,事件按照ViewGroup → View
的正常传递规则(dispatchTouchEvent
→onInterceptTouchEvent
→onTouchEvent
)向下分发。
三、WindowManager 添加的 View 的事件传递路径:无 Activity 中转
通过WindowManager.addView(view, params)
添加的 View(如悬浮窗、Dialog 的 ContentView),其事件传递路径更 "直接",核心原因是:这类 View 所属的 Window 没有关联 Activity 作为Callback
,且根 View 也不是DecorView
(或虽为 DecorView 但无 Activity 回调) 。
具体路径拆解:
-
ViewRootImpl → 根 View :这类 View 会被作为 "根 View" 关联到一个新的
ViewRootImpl
(每个 Window 对应一个ViewRootImpl
)。ViewRootImpl
的mView
就是我们添加的 View(或其包装的根 View,如 Dialog 的DecorView
但无 Activity 回调)。 -
根 View → View 树 :由于根 View 没有关联 Activity 作为
Callback
,其dispatchTouchEvent
会直接走ViewGroup/View
的默认实现,不会经过 Activity 中转:- 若添加的是普通
View
(如TextView
),则直接调用其dispatchTouchEvent
。 - 若添加的是
Dialog
的DecorView
,其mWindow
的Callback
是Dialog
自身(而非 Activity),因此事件会先交给Dialog.dispatchTouchEvent
,再回传给DecorView
继续分发(但始终不会涉及 Activity)。
- 若添加的是普通
四、核心差异原因总结
-
Window 的 Callback 不同:
- Activity 的 Window(
PhoneWindow
)的Callback
是 Activity,因此DecorView
会优先将事件交给 Activity 处理(形成 "中转")。 - WindowManager 添加的 View 所属的 Window(如
PopupWindow
的 Window、自定义 Window)的Callback
是自身(如PopupWindow
)或null
,不会关联 Activity,因此事件无需经过 Activity。
- Activity 的 Window(
-
根 View 的分发逻辑不同:
- Activity 的根 View 是
DecorView
,其dispatchTouchEvent
被重写,强制优先调用Window.Callback
(即 Activity)。 - WindowManager 添加的 View 的根 View(如普通
ViewGroup
、Dialog
的DecorView
)的dispatchTouchEvent
遵循ViewGroup/View
的默认逻辑,无 Activity 相关的分发步骤。
- Activity 的根 View 是
-
Window 的归属不同:
- Activity 的 Window 是 "应用主窗口",与 Activity 生命周期强绑定,事件传递需要 Activity 参与(如
onUserInteraction
等生命周期回调)。 - WindowManager 添加的 View 通常属于 "子窗口" 或 "系统窗口",独立于 Activity 生命周期,事件传递无需依赖 Activity。
- Activity 的 Window 是 "应用主窗口",与 Activity 生命周期强绑定,事件传递需要 Activity 参与(如
总结
-
Activity 中 View 的事件路径 :
ViewRootImpl → DecorView → Activity → PhoneWindow → DecorView → View树
(因 Activity 是 Window 的 Callback,多了中转)。 -
WindowManager 添加的 View 的事件路径 :
ViewRootImpl → 根View → View树
(无 Activity 作为 Callback,直接分发)。
差异的本质是Window 的 Callback 是否为 Activity,以及根 View 是否有针对 Activity 的事件转发逻辑。