浅析:WindowManager添加的 View 的事件传递机制

要理解直接通过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是事件处理的流水线,最终会调用到ViewPostImeInputStageonProcess()方法,在这里完成从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 是PhoneWindowCallback),这导致事件传递多了一层 "Activity 中转"。

具体路径拆解(结合源码):

  1. ViewRootImpl → DecorViewViewRootImplmViewDecorView,调用DecorView.dispatchPointerEvent(event),最终会触发DecorView.dispatchTouchEvent(event)

  2. DecorView → ActivityDecorViewdispatchTouchEvent会优先将事件交给其关联的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);
    }
  3. 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); 
    }
  4. DecorView → View 树 :回到DecorView后,事件按照ViewGroup → View的正常传递规则(dispatchTouchEventonInterceptTouchEventonTouchEvent)向下分发。

三、WindowManager 添加的 View 的事件传递路径:无 Activity 中转

通过WindowManager.addView(view, params)添加的 View(如悬浮窗、Dialog 的 ContentView),其事件传递路径更 "直接",核心原因是:这类 View 所属的 Window 没有关联 Activity 作为Callback,且根 View 也不是DecorView(或虽为 DecorView 但无 Activity 回调)

具体路径拆解:

  1. ViewRootImpl → 根 View :这类 View 会被作为 "根 View" 关联到一个新的ViewRootImpl(每个 Window 对应一个ViewRootImpl)。ViewRootImplmView就是我们添加的 View(或其包装的根 View,如 Dialog 的DecorView但无 Activity 回调)。

  2. 根 View → View 树 :由于根 View 没有关联 Activity 作为Callback,其dispatchTouchEvent会直接走ViewGroup/View的默认实现,不会经过 Activity 中转:

    • 若添加的是普通View(如TextView),则直接调用其dispatchTouchEvent
    • 若添加的是DialogDecorView,其mWindowCallbackDialog自身(而非 Activity),因此事件会先交给Dialog.dispatchTouchEvent,再回传给DecorView继续分发(但始终不会涉及 Activity)。

四、核心差异原因总结

  1. Window 的 Callback 不同

    • Activity 的 Window(PhoneWindow)的Callback是 Activity,因此DecorView会优先将事件交给 Activity 处理(形成 "中转")。
    • WindowManager 添加的 View 所属的 Window(如PopupWindow的 Window、自定义 Window)的Callback是自身(如PopupWindow)或null,不会关联 Activity,因此事件无需经过 Activity。
  2. 根 View 的分发逻辑不同

    • Activity 的根 View 是DecorView,其dispatchTouchEvent被重写,强制优先调用Window.Callback(即 Activity)。
    • WindowManager 添加的 View 的根 View(如普通ViewGroupDialogDecorView)的dispatchTouchEvent遵循ViewGroup/View的默认逻辑,无 Activity 相关的分发步骤。
  3. Window 的归属不同

    • Activity 的 Window 是 "应用主窗口",与 Activity 生命周期强绑定,事件传递需要 Activity 参与(如onUserInteraction等生命周期回调)。
    • WindowManager 添加的 View 通常属于 "子窗口" 或 "系统窗口",独立于 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 的事件转发逻辑。

相关推荐
xiaolizi5674891 小时前
安卓远程安卓(通过frp与adb远程)完全免费
android·远程工作
阿杰100011 小时前
ADB(Android Debug Bridge)是 Android SDK 核心调试工具,通过电脑与 Android 设备(手机、平板、嵌入式设备等)建立通信,对设备进行控制、文件传输、命令等操作。
android·adb
梨落秋霜1 小时前
Python入门篇【文件处理】
android·java·python
遥不可及zzz4 小时前
Android 接入UMP
android
Coder_Boy_6 小时前
基于SpringAI的在线考试系统设计总案-知识点管理模块详细设计
android·java·javascript
冬奇Lab7 小时前
【Kotlin系列03】控制流与函数:从if表达式到Lambda的进化之路
android·kotlin·编程语言
冬奇Lab7 小时前
稳定性性能系列之十二——Android渲染性能深度优化:SurfaceFlinger与GPU
android·性能优化·debug
冬奇Lab8 小时前
稳定性性能系列之十一——Android内存优化与OOM问题深度解决
android·性能优化
用户74589002079549 小时前
线程池
android