android InputManagerService 笔记

4 阅读4分钟

InputManagerService(IMS)是Android系统中整个输入事件机制的中枢。它运行在SystemServer进程,职责是从Linux内核读取原始输入事件,经加工后精确分发给目标窗口。

它的核心精髓可以概括为一句话:Java层薄封装,Native层全逻辑。下面从架构分层、启动流程、事件分发全链路三个维度来拆解。


一、核心架构分层

IMS 90%以上的逻辑在Native层(C++),Java层仅作为系统服务对外暴露接口。

层级核心组件职责
应用层ViewRootImpl / InputEventReceiver接收事件,触发View.dispatchTouchEvent
Java框架层InputManagerService系统服务封装,与WMS、AMS交互,提供API
Native层InputManagerIMS总控,初始化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构造函数的核心动作

  1. Java层:创建InputManagerHandler(运行在android.display线程)
  2. JNI层nativeInit() → 创建NativeInputManager
  3. Native层核心
    • 创建EventHub:通过inotify+epoll监听设备节点增删和事件
    • 创建InputManager → 内部创建InputReaderInputDispatcher
    • 启动InputReaderThreadInputDispatcherThread两个独立线程

为什么是两个线程?
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
    • 手势初步识别、误触滤除

加工后的数据封装为MotionEventKeyEvent,通过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

四、关键知识点补充

  1. WMS与IMS的协作关系
    WMS是“窗口状态的管理者”,IMS是“事件投递的执行者”。WMS通过nativeSetInputWindows将窗口布局、焦点、触摸区域实时同步给Native层的InputDispatcher。

  2. 为什么不直接用Binder?
    输入事件频率高(如120Hz触摸采样)、数据量小、对延迟敏感。Socket(InputChannel)是全双工、无重量级跨进程权限校验的方案,实测延迟更低。

  3. 事件拦截优先级(由高到低):
    系统策略(电源键)→ 系统窗口(状态栏)→ 应用ViewGroup.onInterceptTouchEvent → View.onTouchEvent

  4. 多窗口/折叠屏场景
    IMS基于WMS提供的窗口可见区域做命中测试,非活动窗口不会收到触摸事件。