Android Framework中Input事件的传递流程
Table of Contents
以下是 Android Framework 中 Input 事件传递流程说明,聚焦硬件层、系统服务层和应用进程层的关键组件与流程
1. 硬件层 & Linux 内核
关键组件与流程
-
硬件中断
- 定义:用户触摸屏幕、按下物理按键或移动鼠标时,硬件设备(如触摸屏控制器、键盘驱动)会触发电子信号,生成硬件中断。
- 作用:通知 CPU 有输入事件需要处理,中断当前任务,优先处理输入事件。
-
内核驱动(如
evdev
)- 角色:Linux 内核中的输入设备驱动(如
evdev
)负责将硬件中断转换为标准输入事件。 - 数据格式:事件包含原始信息,例如:
- 触摸事件:坐标(X/Y)、压力值、时间戳。
- 按键事件:按键码(KeyCode)、按下/释放状态。
- 写入节点:驱动将事件写入
/dev/input/eventX
(X 为设备编号)字符设备节点,供用户空间程序读取。
- 角色:Linux 内核中的输入设备驱动(如
-
/dev/input
节点- 功能:Linux 内核通过虚拟文件系统(如
sysfs
和devfs
)暴露输入设备的接口。 - 多设备管理:每个物理输入设备(如触摸屏、物理键盘)对应一个独立的
/dev/input/eventX
节点。
- 功能:Linux 内核通过虚拟文件系统(如
2. 系统服务层(Native 层)
关键组件与流程
-
EventHub
- 作用:监听
/dev/input
目录下的设备节点,管理输入设备的增删(如 USB 设备热插拔)。 - 实现机制:
- 使用
epoll
或inotify
监控设备节点变化。 - 读取设备的配置信息(如屏幕分辨率、按键映射)。
- 使用
- 作用:监听
-
InputReader
- 线程模型:运行在
SystemServer
进程中的独立线程,负责持续从EventHub
读取原始事件。 - 事件解析:
- 将原始数据转换为 Android 标准事件对象(如
MotionEvent
、KeyEvent
)。 - 处理设备校准、坐标转换(如将物理坐标转换为屏幕逻辑坐标)。
- 将原始数据转换为 Android 标准事件对象(如
- 输出:解析后的事件发送给
InputDispatcher
。
- 线程模型:运行在
-
InputDispatcher
- 核心职责:将事件分发给正确的应用窗口。
- 关键流程:
- 确定目标窗口:
- 调用
WindowManagerService (WMS)
,根据窗口的 Z-order、可见性、焦点状态,选择当前接收输入的窗口(如前台 Activity 的根视图)。
- 调用
- 事件策略处理:
- ANR 检测:若应用 5 秒内未处理完事件,触发
Application Not Responding (ANR)
错误。 - 输入过滤:根据系统策略丢弃无效事件(如防误触算法过滤边缘触摸)。
- ANR 检测:若应用 5 秒内未处理完事件,触发
- 跨进程通信:
- 通过
InputChannel
将事件发送到目标应用进程。每个窗口对应一个InputChannel
,本质上是基于Socket
或共享内存
的 IPC 机制。
- 通过
- 确定目标窗口:
-
InputChannel
- 实现原理:
- 服务端:
InputDispatcher
持有InputChannel
的服务端,负责写入事件。 - 客户端:应用进程通过
ViewRootImpl
注册InputChannel
的客户端,用于接收事件。
- 服务端:
- 性能优化:使用共享内存(
ashmem
)减少数据拷贝开销。
- 实现原理:
3. 应用进程层
关键组件与流程
-
ViewRootImpl
- 角色:连接应用窗口与系统服务的桥梁,每个窗口(如 Activity、Dialog)对应一个
ViewRootImpl
实例。 - 初始化
InputChannel
:在窗口创建时,通过WindowManagerGlobal
与系统服务协商建立InputChannel
。
- 角色:连接应用窗口与系统服务的桥梁,每个窗口(如 Activity、Dialog)对应一个
-
WindowInputEventReceiver
- 继承关系:继承自
InputEventReceiver
,负责监听InputChannel
的事件。 - 事件接收:
- 从
InputChannel
读取事件(底层通过Looper
监听文件描述符的可读状态)。 - 将事件封装为
InputEvent
对象(如MotionEvent
)。
- 从
- 继承关系:继承自
-
主线程事件处理
- 线程切换:通过
Handler
将事件包装为Message
,发送到主线程的MessageQueue
。 - 执行逻辑:主线程的
Looper
从MessageQueue
取出事件,调用ViewRootImpl
的dispatchInputEvent()
方法,开始 View 层级分发。
- 线程切换:通过
关键流程总结
-
硬件到内核:
硬件中断 → 内核驱动 → /dev/input节点
-
系统服务处理:
InputReader(读取&解析) → InputDispatcher(确定目标&分发) → InputChannel(跨进程)
-
应用进程接收:
InputChannel → ViewRootImpl → 主线程Handler → View层级分发
深度问答
-
Q1: 为什么需要
EventHub
?
A1: 直接操作/dev/input
节点需处理设备热插拔、多设备并发等问题,EventHub
封装了这些复杂性,提供统一的事件监听接口。 -
Q2:
InputDispatcher
如何避免事件被错误分发?
A2: 通过 WMS 实时获取窗口状态(如焦点窗口、可见性),仅将事件发送给符合条件的窗口,并在窗口失去焦点时终止后续事件。 -
Q3:
InputChannel
为何选择 Socket 或共享内存?
A3: Socket 通用性强,共享内存性能更高。Android 根据不同场景选择:按键事件多用 Socket(低频率),触摸事件可能用共享内存(高吞吐量)。
通过以上流程,Android 实现了从硬件输入到应用响应的完整链路,确保高效、可靠的事件传递。
明天了解一下文中提到的 Linux 驱动究竟是个什么东西