InputManagerService(IMS)是Android系统中整个输入事件机制的中枢。它运行在SystemServer进程,职责是从Linux内核读取原始输入事件,经加工后精确分发给目标窗口。
它的核心精髓可以概括为一句话:Java层薄封装,Native层全逻辑。下面从架构分层、启动流程、事件分发全链路三个维度来拆解。
一、核心架构分层
IMS 90%以上的逻辑在Native层(C++),Java层仅作为系统服务对外暴露接口。
| 层级 | 核心组件 | 职责 |
|---|---|---|
| 应用层 | ViewRootImpl / InputEventReceiver | 接收事件,触发View.dispatchTouchEvent |
| Java框架层 | InputManagerService | 系统服务封装,与WMS、AMS交互,提供API |
| Native层 | InputManager | IMS总控,初始化Reader/Dispatcher及线程 |
| InputReader | 读取并加工原始事件 | |
| InputDispatcher | 确定目标窗口并分发事件 | |
| EventHub | 通过epoll监听/dev/input/event*设备节点 | |
| 内核层 | Linux Input子系统 | 驱动采集硬件信号,写入设备节点 |
| 硬件层 | 触摸屏/键盘等物理设备 | 产生原始电信号 |
二、IMS的启动流程(SystemServer→Native)
IMS属于“其他服务”,在SystemServer.startOtherServices()中被创建:
// SystemServer.java
inputManager = new InputManagerService(context); // 1.Java层创建
wm = WindowManagerService.main(context, inputManager, ...); // 2.传入WMS,建立依赖
ServiceManager.addService(Context.INPUT_SERVICE, inputManager);
IMS构造函数的核心动作:
- Java层:创建
InputManagerHandler(运行在android.display线程) - JNI层:
nativeInit()→ 创建NativeInputManager - Native层核心:
- 创建
EventHub:通过inotify+epoll监听设备节点增删和事件 - 创建
InputManager→ 内部创建InputReader和InputDispatcher - 启动
InputReaderThread和InputDispatcherThread两个独立线程
- 创建
为什么是两个线程?
Reader负责读(阻塞在epoll_wait),Dispatcher负责派发(阻塞在队列取数据)。分离设计保证事件读取不因派发卡顿而延迟,类似“迎宾员只管领位,点餐员只管下单”。
三、输入事件分发全流程(6个阶段)
以最常见的触摸屏点击为例,完整链路如下:
阶段1:硬件→内核(原始信号)
手指触摸 → 触控IC产生电容变化 → 驱动将信号转为input_event结构体 → 写入/dev/input/event0。
📌 调试工具:adb shell getevent 可查看原始坐标流。
阶段2:InputReader读取与加工(Native)
InputReaderThread循环调用EventHub.getEvent()读取设备节点数据- 加工处理:
- 坐标转换:根据窗口布局将屏幕绝对坐标转为窗口相对坐标
- 按键映射:通过
.kl文件将硬件扫描码转为Android keycode(如KEY_HOME) - 手势初步识别、误触滤除
加工后的数据封装为MotionEvent或KeyEvent,通过InputListener接口传给Dispatcher。
阶段3:InputDispatcher决策目标窗口(Native)
- 事件进入
Dispatcher的按优先级排序的队列 - 通过Binder向
WMS查询当前窗口信息:- 焦点窗口(按键事件直送)
- 所有窗口的Z序、可见区域、触摸区域(触摸事件用命中测试)
- 决策结果:
- 系统按键(HOME、音量)→ PolicyManager拦截
- 触摸事件 → Z序最高的命中窗口
- 普通按键 → 焦点窗口
阶段4:跨进程分发(Native → 应用)
IMS不使用Binder传输事件流,因为Binder不适合高频、低延迟的实时数据传输。
- 使用 InputChannel(基于Unix Domain Socket)
- 每个窗口在创建时,WMS会请求IMS为其创建一对Socket Pair:
- 服务端(IMS.InputChannel)
- 客户端(ViewRootImpl.InputChannel)
- Dispatcher将事件序列化后写入Socket,应用端
InputEventReceiver通过epoll唤醒读取
阶段5:应用层消费与反馈(Java)
ViewRootImpl$ViewInputEventReceiver收到事件 →dispatchInputEvent- View体系事件分发(DOWN决定全局消费链):
Activity.dispatchTouchEvent → PhoneWindow → DecorView.dispatchTouchEvent → ViewGroup/View - 消费反馈:若
onTouchEvent返回true,ViewRootImpl通过InputChannel回传“已消费”;否则IMS尝试分发到下一个候选窗口 - ANR监控:Dispatcher会监控事件处理超时(默认5秒),超时则通过AMS触发ANR
阶段6:收尾与异常处理
- 无窗口消费 → 事件直接丢弃
- 屏幕休眠唤醒:触摸事件会先通知PowerManagerService唤醒屏幕,再分发事件
- 窗口销毁 → 自动关闭对应的InputChannel
四、关键知识点补充
-
WMS与IMS的协作关系
WMS是“窗口状态的管理者”,IMS是“事件投递的执行者”。WMS通过nativeSetInputWindows将窗口布局、焦点、触摸区域实时同步给Native层的InputDispatcher。 -
为什么不直接用Binder?
输入事件频率高(如120Hz触摸采样)、数据量小、对延迟敏感。Socket(InputChannel)是全双工、无重量级跨进程权限校验的方案,实测延迟更低。 -
事件拦截优先级(由高到低):
系统策略(电源键)→ 系统窗口(状态栏)→ 应用ViewGroup.onInterceptTouchEvent → View.onTouchEvent -
多窗口/折叠屏场景
IMS基于WMS提供的窗口可见区域做命中测试,非活动窗口不会收到触摸事件。