输入问题排查:从设备到 View 的五层检查链
输入问题排查:从设备到 View 的五层检查链
前七章把输入主链拆开讲完了。这一章把它们串成一条可执行的检查链:触摸失灵、按键无响应、 输入 ANR,按什么顺序查、看什么证据、怎么定位。
“View 没收到事件”只是最终现象。事件可能从未离开设备节点,也可能在 reader 转换、窗口 选择、通道投递或应用主线程消费阶段停住。定位的关键是找到最后一层确实存在的证据。
先确认事件在哪一层消失
| 常见误解 | 正确理解 |
|---|---|
| 触摸失灵一定是 View 代码问题 | 可能是设备、reader、dispatcher、窗口目标任一一环断了 |
dumpsys input 是日志 | 它是输入系统状态快照,和 logcat 是两种证据 |
| 输入 ANR 一定表示应用死循环 | 也可能是事件根本没被消费,或主线程被其他任务卡住 |
固定从设备向应用推进:先用 getevent 验证原始上报,再查 InputReader 与 InputDispatcher 状态,核对目标窗口和通道,最后查看应用主线程与 View 分发。每一步都以前一步成立为前提。
核心结论
- 检查顺序固定:设备/原始事件 → reader 加工 → dispatcher 分发 → 窗口目标 → 应用消费。
getevent验证硬件有没有上报;dumpsys input验证加工与分发状态;ANR trace 验证 应用有没有消费。- “点不到”先查窗口元数据(bounds、touchableRegion、FLAG_NOT_TOUCHABLE),再进 View。
- 输入 ANR 的根因分两类:dispatcher 找不到/送不到目标,或应用收到但没消费。
- 修复后要复现验证:切换方向、多窗口、锁屏解锁等场景都要回归。
五层检查链
逐步解释:
- 设备层:
getevent看内核原始事件,确认硬件有没有上报。 - reader 层:
dumpsys input的 Reader State 看设备与配置,确认事件有没有被加工。 - dispatcher 层:Dispatcher State 看入站/出站队列,确认事件有没有投递与超时。
- 窗口层:
dumpsys window与 dispatcher 窗口表看目标与焦点。 - 应用层:ANR trace 与 logcat 看应用有没有接收和消费。
常用命令与证据
环境:开发设备或模拟器;以下命令不需要 root。
adb shell getevent -lt # 第 1 层:原始事件
adb shell dumpsys input | sed -n '/Input Reader State/,/Input Dispatcher State/p' # 第 2 层
adb shell dumpsys input | sed -n '/Input Dispatcher State/,/Registered input channels/p' # 第 3 层
adb shell dumpsys window | grep -i focus # 第 4 层:焦点
adb shell dumpsys input | grep -iE "windows:|touchableRegion" # 第 4 层:窗口表
adb shell ls /data/anr/ && adb pull /data/anr/ # 第 5 层:ANR trace
adb logcat -v threadtime | grep -E "InputDispatcher|InputReader|InputEventReceiver"| 证据 | 含义 | 下一步 |
|---|---|---|
getevent 无输出 | 硬件或驱动没上报 | 查设备、驱动、节点权限 |
| Reader State 无事件 | 加工环节异常 | 查设备配置与策略 |
| Dispatcher 显示投递但应用无日志 | 通道或接收环节问题 | 查通道注册与 Looper |
| 窗口表无目标窗口 | 窗口元数据未同步 | 查 WMS 窗口状态 |
| ANR trace 显示主线程卡住 | 应用消费不及时 | 定位卡点并优化 |
| 窗口有 FLAG_NOT_TOUCHABLE | 触摸被让给下层 | 核对窗口属性与业务意图 |
常见场景与定位思路
场景一:整个屏幕触摸无反应
先 getevent:无原始事件说明设备/驱动问题;有事件但无分发,继续看 Reader/Dispatcher。 如果连系统手势都没反应,优先怀疑输入系统整体,而不是单个应用。
场景二:某个区域点不到
用 dumpsys input 看该坐标命中的窗口与 touchableRegion;再确认是否有窗口覆盖但 FLAG_NOT_TOUCHABLE,或上层窗口 bounds 吞掉了事件。这类问题根因常在窗口层,不在 View。
场景三:按键无响应
先确认焦点窗口:dumpsys window 的 mCurrentFocus。焦点在别的窗口时,按键不会到目标 应用;再确认应用是否消费了 KeyEvent(onKeyDown 返回 false 也会继续向上)。
场景四:输入 ANR
输入 ANR 说明 dispatcher 投递后应用未按时消费。看 /data/anr/ trace:主线程卡在 什么位置、是否在处理上一事件、是否有死锁。修复方向是让主线程及时消费,而不是调大 超时阈值。
常见误区
- “触摸问题都是 View 问题”:先走五层检查链,别跳步。
- “
dumpsys input是日志”:它是状态快照,配合 logcat 和 trace 一起看。 - “焦点正确就一定能收到触摸”:触摸看命中测试,不看焦点。
- “调大 ANR 超时能治本”:超时只是观测窗口,根因是消费不及时。
- “注入事件能代表真实触摸”:
input tap走注入路径,与真实硬件路径有差异。
延伸问题
- Perfetto 如何抓取输入事件的完整时间线?
- 触摸抖动、漂移问题应该在哪个环节加滤波或校准?
- 多用户或折叠屏场景下,窗口元数据如何切换?
- 如何为输入问题设计可复现的自动化回归用例?
源码入口
| 文件 | 关键位置 | 作用 |
|---|---|---|
EventHub.cpp | getEvents() | 原始事件读取 |
InputReader.cpp | loopOnce() | 事件加工 |
InputDispatcher.cpp | dispatchOnce() / findTouchedWindowAtLocked() | 分发与命中 |
InputEventReceiver.java | onInputEvent() / finishInputEvent() | 应用接收与消费 |
ViewRootImpl.java | deliverPointerEvent() | 进入 View 分发 |
公共路径沿用本系列各章:frameworks/base/services/inputflinger/、 frameworks/base/core/java/android/view/。行号以 r75 检索为准。
窗口侧状态的生产与更新链路,见 WMS 窗口生命周期。