触摸事件为什么会送到这个窗口
触摸事件为什么会送到这个窗口
WMS 不直接把 MotionEvent 发给应用。它把窗口层级、触摸区域、可见性、焦点和安全属性整理成 输入窗口信息;Native InputDispatcher 在事件到达时基于这份快照选择目标,再通过 InputChannel 把事件送进应用进程。
WMS 提供窗口地图,InputDispatcher 选择目标
| 常见误解 | 正确理解 |
|---|---|
| WMS 决定触摸事件送给谁 | WMS 只提供窗口元数据,最终命中由 InputDispatcher 在 native 侧完成 |
| 焦点窗口一定收到触摸 | 触摸按坐标命中,和按键(按焦点)是两套选择逻辑 |
按键看焦点,触摸看位置。InputDispatcher 手里有一份“窗口地图”,每个窗口标注可触摸区域、层级和可见性;手指按下时,它在地图上做命中测试。
信息流主链
逐步解释:
- ① 状态变化:布局、可见性或层级变化后,WMS 标记需要更新输入窗口信息。
- ② 收集:
InputMonitor.updateInputWindowsLw()(InputMonitor.java:333)遍历显示器窗口。 - ③ 填充:
accept()(同文件 619 行)为每个可接收输入的窗口填充 InputWindowHandle。 - ④ 下发:
setInputWindowInfoIfNeeded()(同文件 696 行)通过Transaction.setInputWindowInfo()把信息随事务带到 native。 - ⑤ 命中:InputDispatcher 的
findTouchedWindowAtLocked()(InputDispatcher.cpp:1355)按坐标选择目标。 - ⑥ 派发:事件经目标窗口的 InputChannel 送到应用。
三个关键概念
| 概念 | 是什么 | 对目标选择的影响 |
|---|---|---|
InputWindowHandle | 一个窗口的输入元数据(frame、touchableRegion、可见性、层级) | 导览图上的店铺标注 |
InputChannel | 输入事件从系统到应用的通道 | 店铺大门 |
TouchableRegion | 窗口可接收触摸的区域,可能与 frame 不同 | 店铺门口的可站区域 |
TouchableRegion 很重要:一个窗口的视觉范围不等于可触摸范围,比如透明遮罩窗口可以把触摸区域设为空。
焦点与触摸的配合
按键事件按 mCurrentFocus 派发;触摸事件按坐标命中。焦点窗口与触摸目标不一致是正常情况:Dialog 有焦点,用户点背景时触摸目标却是 Dialog 之下的窗口——除非背景窗口不可触摸或被安全策略拦截。
源码证据
阅读目标:确认 WMS 的职责止于“提供元数据”,命中在 native 侧。
正文指针:InputMonitor.java:333 / 619 / 696;SurfaceControl.java:3107;InputDispatcher.cpp:1355。
// InputMonitor.java:333
void updateInputWindowsLw(boolean force) {// SurfaceControl.java:3107
public Transaction setInputWindowInfo(SurfaceControl sc, InputWindowHandle handle) {// InputDispatcher.cpp:1355
sp<WindowInfoHandle> InputDispatcher::findTouchedWindowAtLocked(int32_t displayId, float x, float y,这段代码证明: WMS 侧构造并下发 InputWindowHandle,输入信息经事务进入 native;真正按坐标选目标的函数在 InputDispatcher 里。
输入通道从哪来
窗口加窗时就可能打开输入通道:win.openInputChannel(outInputChannel)(WindowManagerService.java:1682)。没有通道的窗口(如 INPUT_FEATURE_NO_INPUT_CHANNEL)不会出现在触摸命中的候选里。
源码入口
源码基准:android-14.0.0_r75。
以下文件默认位于 frameworks/base/,表中省略该前缀:
| 文件 | 关键位置 | 作用 |
|---|---|---|
services/core/java/com/android/server/wm/InputMonitor.java | 333 行 updateInputWindowsLw();575 行 updateInputWindows();619 行 accept();688 行 populateInputWindowHandle();696 行 setInputWindowInfoIfNeeded() | 输入窗口信息收集 |
core/java/android/view/SurfaceControl.java | 3107 行 Transaction.setInputWindowInfo() | 输入信息事务下发 |
services/core/java/com/android/server/wm/WindowManagerService.java | 1682 行 win.openInputChannel(...) | 输入通道创建 |
frameworks/native/services/inputflinger/dispatcher/InputDispatcher.cpp | 5238 行 setInputWindowsLocked();1355 行 findTouchedWindowAtLocked() | native 侧目标选择 |