整个流程可以概括为以下核心架构图,它展示了输入事件的数据流和核心组件:

第一阶段:内核空间 - /dev/input/eventX
- 硬件中断:当用户触摸屏幕、按下物理按键或移动鼠标时,硬件设备(触摸屏、键盘控制器等)会产生一个硬件中断(IRQ)
- 设备驱动:对应的输入设备驱动会处理这个中断,将原始的硬件信号(如触摸点的X、Y坐标、按键码、压力值等)封装成一个标准格式的数据结构(
struct input_event)struct input_event { struct timeval time; // 时间戳 __u16 type; // 事件类型 (e.g., EV_KEY, EV_ABS, EV_SYN) __u16 code; // 事件代码 (e.g., BTN_TOUCH, ABS_X, KEY_VOLUMEDOWN) __s32 value; // 事件值 (e.g., 坐标值, 0/1表示按下/松开) }; - 写入设备节点:驱动将这个
input_event写入到对应的/dev/input/eventX(X通常是数字,如event0)字符设备节点中。这个节点是内核空间与用户空间进行交互的接口
第二阶段:用户空间 - EventHub 和 InputReader
这两个组件在 system_server 进程中,是 InputManagerService 的核心组成部分
1. EventHub
- 职责:监控和读取所有
/dev/input/目录下的输入设备节点。它是硬件事件的直接来源 - 工作机制:
○ 使用inotify监控/dev/input/目录,以便在设备热插拔(如USB键盘插入拔出)时动态添加或移除设备
○ 使用epoll机制轮询所有已打开的输入设备节点。当某个设备有数据可读时(即用户有输入动作),epoll会通知EventHub
○EventHub从对应的设备节点中读出原始的input_event数据,并将其简单封装为一个RawEvent对象
○InputReader线程会不断地从EventHub中getEvents(),获取这些RawEvent队列
2. InputReader
- 职责:解析和加工
RawEvent。它是理解输入事件含义的“翻译官” - 工作机制:
○ 它运行在一个独立的线程(InputReader Thread)中,不断循环调用EventHub->getEvents(),拿到一揽子RawEvent
○ 它持有所有输入设备的配置信息(InputDevice),如键盘布局、触摸屏校准参数、按键映射等
○ 根据RawEvent的type和code,将多个连续的原始事件组合成一个有逻辑意义的输入事件
◎ 例如:一个触摸操作通常会产生多个RawEvent:一个EV_ABS(ABS_X) 事件报告X坐标,一个EV_ABS(ABS_Y) 事件报告Y坐标,一个EV_KEY(BTN_TOUCH) 事件报告按下状态,最后是一个EV_SYN(SYN_REPORT) 事件表示报告结束。InputReader会等待SYN_REPORT到来后,才认为一个完整的触摸点数据准备好了
○ 加工完成后,InputReader会生成更高级的、Android框架定义的事件对象,如KeyEvent(按键事件)、MotionEvent(触摸/运动事件)
○ 策略处理:在这个过程中,它会应用一些输入策略,如:
◎ 设备映射:将游戏手柄的某个按键映射为“返回”键
◎ 输入校准:对触摸屏坐标进行校准
◎ 虚拟键处理:处理一些设备的电容导航键
○ 最后,InputReader调用InputDispatcher->notifyXXX()方法,将加工好的事件放入InputDispatcher的队列中
第三阶段:用户空间 - InputDispatcher
- 职责:事件的派发和仲裁。它是输入事件的“交通警察”,决定事件最终发给哪个应用窗口
- 工作机制:
○ 它运行在另一个独立的线程(InputDispatcher Thread)中
○ 它维护了一个来自InputReader的输入事件队列
○ 寻找目标窗口:当一个新事件到来时,InputDispatcher会根据当前系统的状态(哪些窗口是活动的、它们的布局位置、焦点状态等)来计算事件应该派发给哪个窗口(WindowState)
◎ 焦点窗口:按键事件通常派发给当前获得焦点的窗口(如输入框)
◎ 触摸命中测试:触摸事件会根据窗体的位置和触摸点坐标进行命中测试(Hit Test)。InputDispatcher会向WindowManagerService(WMS) 查询,找到包含该坐标点的、最顶层的、可见的窗口
○ 应用策略:在此阶段,会应用一些系统策略,如:
◎ 输入超时:防止应用ANR(Application Not Responding)。如果窗口在5秒内没有处理完一个输入事件,系统会弹出ANR对话框
◎ 事件过滤:过滤掉一些系统级快捷键(如Power键、Volume键),这些事件可能会被系统优先消费
◎ 虚拟键处理:Home、Back、Recents键的处理逻辑
○ 派发事件:
◎ 目标窗口确定后,InputDispatcher会通过 SocketPair(一个双向的IPC通道)将事件发送到目标窗口所在的应用进程
◎ 派发是异步的。InputDispatcher会等待应用处理完毕的“反馈”(finishInputEvent)
◎ 如果事件是按键事件(如Back键),InputDispatcher会先执行一个 “预派发” (Pre-dispatching)流程,将事件先发给当前焦点View,如果它处理了,流程结束;如果没处理,再继续正常的派发流程,可能会最终交给Activity处理
第四阶段:应用进程 - 从 ViewRootImpl 到 View
事件现在离开了 system_server,进入了目标应用进程
- Socket监听与接收:
○ 每个应用的UI主线程都通过Looper监听一个特殊的InputChannel(它封装了SocketPair)
○ 当InputDispatcher将事件写入Socket后,应用端的InputChannel就会收到通知 ViewRootImpl$WindowInputEventReceiver:
○ 在应用端,ViewRootImpl内部的WindowInputEventReceiver的onInputEvent()方法被调用,它接收到了原始的输入事件InputEventReceiver和InputStage:
○ViewRootImpl内部有一个InputStage责任链模式的处理管道。事件会依次经过不同的InputStage,每个阶段都有机会处理或消费事件
○ 阶段示例:
◎NativePreImeStage:处理本地预输入法事件(如手柄按键)
◎ViewPreImeStage:View树的预输入法阶段
◎ImeStage:将事件派发给输入法(IME)。如果当前有输入法窗口,按键事件会先到这里。输入法可以消费掉事件(如输入文字),也可以选择不消费并让事件继续传递(如按下的方向键)
◎EarlyPostImeStage:早期后输入法阶段,处理一些原始事件
◎ViewPostImeStage:这是最关键的一步。在这里,事件被封装成Java层的KeyEvent或MotionEvent,并开始进入View树的分发流程- View树分发:
○ 起点:分发从DecorView(根View)的dispatchPointerEvent()开始
○ 传递路径:DecorView->PhoneWindow->ContentView(通常是FrameLayout) -> ... -> 最终的目标View
○ 分发规则:
◎ 触摸事件 (MotionEvent):遵循onInterceptTouchEvent和onTouchEvent的机制。从父View到子View传递,父View可以拦截(onInterceptTouchEvent返回true),子View可以消费(onTouchEvent返回true)
◎ 按键事件 (KeyEvent):从子View到父View传递。首先会尝试分发给当前获得焦点的View,如果它没有消费,则会依次向上传递给它的父View - 事件消费与反馈:
○ 当事件被View树中的某个View的onTouchEvent或onKeyEvent方法处理(返回true)后,处理流程结束
○ 反馈:处理完成后,会沿着原路返回一个“完成”的信号(finishInputEvent),通过InputChannel和SocketPair最终回传给InputDispatcher
○InputDispatcher收到反馈后,才会继续派发下一个输入事件。如果超时未收到反馈,就会触发ANR
总结与特点
- 生产者-消费者模型:
InputReader是生产者,不断生产事件;InputDispatcher是消费者和二次生产者;应用是最终的消费者 - 多线程设计:
InputReader和InputDispatcher是两个独立的线程,避免了事件读取阻塞事件派发 - 异步非阻塞:派发过程是异步的,通过SocketPair进行IPC,效率高
- 中心化仲裁:
InputDispatcher和WindowManagerService协同工作,是输入事件的唯一仲裁中心,确保了系统级策略的统一应用 - ANR机制:超时机制保证了系统的响应性