4. Android输入系统分层
Android 输入系统并不是一个“从驱动直接到 App”的短链路,而是一条跨越四个软件层级、经过多次格式转换与语义增强的长链路。理解这条链路的分层职责,是排查“点击无响应”“滑动丢帧”“坐标偏移”“事件被拦截”等问题的前提。本节围绕 Kernel 层、Native 层、Java 层、应用层四层展开,重点说明每层“负责什么、不负责什么”,以及事件对象在各层之间的形态变化。
一、四层职责总览
从物理触摸到应用回调,输入事件经历的是“硬件信号 → 内核事件 → Native 事件 → Java 事件 → 应用语义”的逐级抽象过程。每一层只解决自己范围内的问题,层与层之间通过明确的接口解耦。
| 层级 | 核心职责 | 典型对象/接口 | 不负责的内容 |
|---|---|---|---|
| Kernel 层 | 驱动触摸屏/按键,产生原始输入事件,写入 evdev 节点 | input_dev、input_event、/dev/input/eventX | 不做坐标映射、不做手势识别 |
| Native 层 | 读取 evdev、解析原始事件、生成 NotifyArgs、分发到窗口 | InputReader、InputDispatcher、InputChannel | 不决定应用如何消费事件 |
| Java 层 | 窗口管理、输入通道注册、事件到 View 树的派发 | WindowManagerService、InputManagerService、ViewRootImpl | 不直接读取硬件节点 |
| 应用层 | 消费 MotionEvent/KeyEvent,实现业务交互逻辑 | onTouchEvent、onClick、GestureDetector | 不感知底层设备与通道细节 |
二、Kernel 层:原始事件的产生者
Kernel 层的输入子系统由设备驱动和 input core 组成。触摸屏驱动在探测到触点后,通过 input_report_abs()、input_report_key() 等接口上报事件,最终由 input core 写入对应的 evdev 节点。此时的事件是“设备视角”的原始数据,例如 ABS_MT_POSITION_X、ABS_MT_POSITION_Y、BTN_TOUCH、SYN_REPORT。
// 内核侧典型上报序列(简化)
input_report_abs(ts->input, ABS_MT_POSITION_X, x);
input_report_abs(ts->input, ABS_MT_POSITION_Y, y);
input_report_key(ts->input, BTN_TOUCH, 1);
input_sync(ts->input); // 对应 SYN_REPORT,标志一帧结束
这一层的关键特征是:事件以“设备能力”为单位,坐标是原始坐标,尚未经过屏幕旋转、显示缩放、多屏映射等处理。因此,如果驱动上报的坐标范围与屏幕分辨率不一致,问题往往会在 Native 层被放大。
三、Native 层:输入系统的中枢
Native 层是 Android 输入系统真正的“大脑”,主要由 InputReader 和 InputDispatcher 两个线程协作完成。InputReader 通过 EventHub 读取 evdev 节点,把原始 input_event 转换为内部事件,并做坐标映射、设备校准、触摸点跟踪;InputDispatcher 则负责找到目标窗口,通过 InputChannel 把事件发送给应用。
- EventHub:监听 /dev/input 下的设备节点,负责打开、读取、热插拔管理。
- InputReader:解析原始事件,生成 NotifyMotionArgs、NotifyKeyArgs 等参数对象。
- InputDispatcher:维护窗口焦点、触摸目标、ANR 超时,执行事件分发。
- InputChannel:基于 socketpair 的双向通道,是 Native 与 Java 之间的桥梁。
需要特别注意的是,InputDispatcher 并不直接把事件“推”给某个 View,它只负责把事件送到目标窗口对应的 InputChannel。窗口内部如何继续派发,属于 Java 层和应用层的职责。
四、Java 层与应用层:从窗口到 View 树
Java 层的核心是 InputManagerService 与 WindowManagerService 的配合。InputManagerService 负责启动 Native 输入系统、注册输入通道;WindowManagerService 负责窗口层级、焦点窗口和触摸区域的计算。每个窗口在创建时,会通过 InputChannel 与 Native 层建立连接。
应用进程侧,ViewRootImpl 持有 InputChannel 的客户端,通过 WindowInputEventReceiver 接收事件,再交给 ViewPostImeInputStage 处理,最终调用到 DecorView 及 View 树的 dispatchTouchEvent()。应用层开发者最常接触的 MotionEvent、KeyEvent 就是在这里产生的。
// 应用层典型消费入口
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getActionMasked()) {
case MotionEvent.ACTION_DOWN:
// 按下处理
return true;
case MotionEvent.ACTION_MOVE:
// 滑动处理
return true;
case MotionEvent.ACTION_UP:
// 抬起处理
return true;
}
return super.onTouchEvent(event);
}
五、数据流转关系全景
下面这张图把四层职责与事件形态变化放在同一条链路上,便于建立整体认知。图中同时标出了每层的输入对象与输出对象,以及层与层之间的关键接口。
六、案例与常见误区
假设用户反馈“点击按钮偶尔无响应”。按分层思路排查,可以快速缩小范围:
- Kernel 层:用
getevent -lt /dev/input/eventX确认按下/抬起事件是否完整上报,是否存在丢 SYN。 - Native 层:查看 InputReader 是否识别到设备,InputDispatcher 是否找到目标窗口,是否出现 “Dropping event” 或 ANR 超时。
- Java 层:确认窗口焦点、触摸区域、InputChannel 是否正常,是否存在窗口切换导致事件被取消。
- 应用层:检查
onTouchEvent返回值、父 View 是否拦截、是否在主线程做了耗时操作。
常见误区:把“事件没到应用”直接归因于应用代码。实际上,Kernel 丢帧、Native 分发超时、Java 层窗口焦点错误,都会表现为“应用没收到事件”。分层排查比盲目改代码更有效。
记忆口诀:Kernel 产事件,Native 管分发,Java 建通道,应用做消费。四层各司其职,任何一层异常都会在最终交互上体现出来。
掌握四层职责划分后,后续学习 InputReader 解析细节、InputDispatcher 分发策略、InputChannel 通信机制时,就能把每个知识点准确挂到对应层级上,形成完整的输入系统知识地图。