Android 7系统输入(一):从硬件到应用的事件旅程
系列目录:第一篇:从硬件到应用的事件旅程| 第二篇:EventHub | 第三篇:InputReader | 第四篇:InputDispatcher | 第五篇:应用侧
一、为什么要研究输入系统
做过 Android 开发的朋友应该都跟onTouchEvent()和dispatchTouchEvent()打过交道。滑动冲突、点击穿透、ANR 弹窗……这些日常问题背后,都藏着一个庞大而精密的事件分发体系。
但如果你只停留在应用层的事件分发机制,很多深层问题会无从下手:
- 为什么有时候
onTouchEvent迟迟收不到事件?是 InputDispatcher 卡了还是主线程消息队列满了? - "应用无响应"弹窗背后的输入超时机制到底是怎么判定的?
- 插上一个外接键盘,Android 怎么做到"即插即用"地开始接收按键?
- 一个触摸事件从手指按下到
View.onTouchEvent()被调用,中间到底经过了多少层?
这些问题,只有把 Android 输入系统的全链路搞清楚,才能彻底理解。本系列将以AOSP 7(android-7.1.2_r36)为基准源码版本,带你从内核驱动一路走到应用层 View 树,逐层拆解 Android 输入子系统。
本篇是整个系列的总览与导航,先帮你建立起完整的宏观认知。
二、宏观架构:一张图看懂事件流水线
在 Android 输入系统中,一个事件的生命周期可以用一条清晰的流水线来描述:
硬件驱动 --> Linux Kernel --> EventHub --> InputReader (触摸屏) /dev/input/ (Native) (Native) | NotifyArgs | v APP进程 <-- InputDispatcher <-- InputManager ViewRootImpl (socket/Channel) Service(Java)或者用更直观的分层视角来看:
| 层级 | 组件 | 进程 | 职责 |
|---|---|---|---|
| 内核层 | Linux Input 子系统 | Kernel | 驱动硬件,向/dev/input/eventX写入input_event |
| Native 采集层 | EventHub | system_server | 监听设备热插拔,读取原始事件 |
| Native 加工层 | InputReader | system_server | 将原始数据转换为KeyEvent/MotionEvent |
| Native 分发层 | InputDispatcher | system_server | 确定目标窗口,通过 socket 发送事件,监控 ANR |
| Java 管理层 | InputManagerService | system_server | 与 WMS 联动,管理输入法、手势等策略 |
| 应用接收层 | ViewRootImpl | APP 进程 | 从 socket 接收事件,沿 View 树分发 |
三、内核层:Linux Input 子系统的角色
3.1 /dev/input/ 设备节点
当你插上一个 USB 键盘或者设备自带触摸屏时,Linux 内核会在/dev/input/目录下创建对应的设备节点:
$ls/dev/input/ event0 event1 event2 event3 event4 mice mouse0每个eventX都对应一个输入设备。可以用getevent命令实时查看原始事件:
$ adb shell getevent-lt/dev/input/event2# 触摸屏按下[123456.789]EV_ABS ABS_MT_TRACKING_ID 00000045[123456.789]EV_ABS ABS_MT_POSITION_X 00000320[123456.789]EV_ABS ABS_MT_POSITION_Y 00000580[123456.789]EV_SYN SYN_REPORT 00000000# 触摸屏抬起[123457.123]EV_ABS ABS_MT_TRACKING_ID ffffffff[123457.123]EV_SYN SYN_REPORT 000000003.2 input_event 结构体
内核中所有输入事件都用一个统一的结构体来描述:
源码路径:kernel/include/linux/input.h
structinput_event{structtimevaltime;// 时间戳__u16 type;// 事件类型(EV_KEY、EV_ABS、EV_SYN 等)__u16 code;// 事件编码(如 KEY_HOME、ABS_MT_POSITION_X)__s32 value;// 事件值(按键:0抬起/1按下;坐标:具体像素值)};Android 后续的所有事件处理,起点都是从这个结构体开始的。
四、Native 核心层:InputFlinger 三剑客
AOSP 源码中,输入系统的 Native 核心代码位于:
frameworks/native/services/inputflinger/ ├── EventHub.cpp / .h # 设备管理与原始事件读取 ├── InputReader.cpp / .h # 事件加工与转换 ├── InputDispatcher.cpp / .h # 事件分发与超时控制 ├── InputManager.cpp / .h # 三者的协调者 ├── InputListener.cpp / .h # 监听器接口 └── InputWindow.cpp / .h # 窗口信息这三个核心组件运行在system_server进程中,各自有独立的线程:
源码路径:frameworks/native/services/inputflinger/InputManager.cpp
status_tInputManager::start(){// 先启动 InputDispatcher,再启动 InputReaderstatus_t result=mDispatcherThread->run("InputDispatcher",PRIORITY_URGENT_DISPLAY);if(result){ALOGE("Could not start InputDispatcher thread due to error %d.",result);returnresult;}result=mReaderThread->run("InputReader",PRIORITY_URGENT_DISPLAY);if(result){ALOGE("Could not start InputReader thread due to error %d.",result);mDispatcherThread->requestExit();returnresult;}returnOK;}注意两个线程都是PRIORITY_URGENT_DISPLAY优先级,Google 对输入响应速度要求非常高——用户每一次触摸都期望"跟手"。
4.1 EventHub:原始事件采集者
EventHub 是整个输入流水线的第一站。它的核心职责非常纯粹:
- 设备发现:通过 Linux 的
inotify机制监听/dev/input/目录,当设备插入/拔出时自动感知 - 设备打开:通过
ioctl获取设备能力信息(支持哪些按键、哪些坐标轴等) - 事件读取:通过
epoll_wait监听所有设备文件描述符,有数据时用read()读取input_event - 事件上报:将读取到的原始事件封装为
RawEvent,传递给InputReader
关键方法签名:
源码路径:frameworks/native/services/inputflinger/EventHub.h
classEventHub{public:size_tgetEvents(inttimeoutMillis,RawEvent*buffer,size_t bufferSize);private:intmEpollFd;// epoll 实例intmINotifyFd;// inotify 实例KeyedVector<int,Device*>mDevices;// 设备列表};(第二篇会详细展开 EventHub 的全部细节。)
4.2 InputReader:事件加工厂
InputReader 拿到 RawEvent 后,需要完成从"内核原始数据"到"Android 可以理解的事件"的转换。这个转换过程远比想象中复杂:
- 一个触摸屏上报的是绝对坐标
(320, 580),但 Android 需要知道这是相对于哪个屏幕、坐标系是否需要旋转 - 一个外接键盘的上报是 Linux 键码
KEY_A = 30,需要转换为 Android 键码AKEYCODE_A = 29 - 多点触控需要跟踪每个手指的
trackingId,识别 DOWN / MOVE / UP / CANCEL 等动作
InputReader 通过InputMapper 体系来处理不同类型的设备:
InputReader └── InputDevice (代表一个物理设备) └── InputMapper (设备类型对应的处理策略) ├── SwitchInputMapper // 开关类 ├── KeyboardInputMapper // 键盘 ├── CursorInputMapper // 鼠标/轨迹球 ├── TouchInputMapper // 触摸屏 │ ├── SingleTouchInputMapper │ └── MultiTouchInputMapper ├── JoystickInputMapper // 摇杆 └── ExternalStylusInputMapper // 外接手写笔加工完成后,事件被封装为NotifyArgs系列对象,通过InputListener接口传递给InputDispatcher。
(第三篇会深入拆解 InputReader 和 Mapper 体系。)
4.3 InputDispatcher:事件分发调度器
InputDispatcher 是输入系统中最关键也最容易出问题的一环。它的核心任务是:
- 接收加工后的事件:从 InputReader 拿到 NotifyArgs
- 确定目标窗口:根据当前焦点窗口 / 触摸坐标找到应该接收事件的窗口
- 通过 InputChannel 发送:利用 socket pair 将事件跨进程发送给 APP
- 超时监控与 ANR:记录每个事件的分发时间,超时则触发 ANR
一个关键的数据结构是 InputChannel:
源码路径:frameworks/native/libs/input/InputTransport.cpp
status_tInputChannel::openInputChannelPair(constString8&name,sp<InputChannel>&outServerChannel,sp<InputChannel>&outClientChannel){intsockets[2];socketpair(AF_UNIX,SOCK_SEQPACKET,0,sockets);// 创建 socket 对...}system_server 通过 socket 的 server 端发送事件,APP 进程持有 client 端来接收事件。这种设计使得事件传输不经过 Binder 驱动,避免了 Binder 线程池调度带来的延迟,保证输入事件的低延迟传输。
(第四篇会全面剖析 InputDispatcher 的分发逻辑与 ANR 机制。)
五、Java 管理层:InputManagerService
InputManagerService(IMS)是输入系统在 Java 层的管理入口:
frameworks/base/services/core/java/com/android/server/input/InputManagerService.java它并不直接参与事件的分发和加工,而是扮演"管理者"的角色:
- 提供 Binder 接口供外部(如 WindowManagerService、Settings 应用)调用
- 管理输入法(IME)的切换与状态
- 处理按键的"特殊行为"(如音量键的拦截策略、电源键的长按关机)
- 向 Native 层传递策略配置(如 ANR 超时时长、触摸事件的坐标偏移等)
IMS 在 SystemServer 启动时被创建,与 WindowManagerService 紧密绑定——WMS 负责窗口的布局与层级,IMS 负责把事件投递到正确的窗口。
六、应用接收层:从 InputChannel 到 View.onTouchEvent
当 InputDispatcher 通过 socket 把事件发送到 APP 进程后,应用侧的处理链路是这样的:
InputChannel (socket client 端) │ ▼ NativeInputEventReceiver (JNI 层,监听 socket fd) │ ▼ InputEventReceiver (Java 层,dispatchInputEvent) │ ▼ ViewRootImpl.WindowInputEventReceiver (子类,处理输入事件) │ ▼ ViewRootImpl.processPointerEvent / processKeyEvent │ ▼ DecorView.dispatchTouchEvent / dispatchKeyEvent │ ▼ Activity.dispatchTouchEvent │ ▼ ViewGroup.dispatchTouchEvent → onInterceptTouchEvent → child.dispatchTouchEvent │ ▼ View.onTouchEvent其中,NativeInputEventReceiver 在 JNI 层通过向主线程的消息队列注册一个fd 监听来实现事件接收。这意味着输入事件实际上是通过消息队列(MessageQueue)被注入到主线程的,它会和主线程上的其他 Message 一起排队等待处理。这也是为什么主线程卡顿会导致触摸事件延迟响应的根本原因。
(第五篇会完整展开应用侧的事件分发机制。)
七、一个触摸事件的完整旅程
用户在屏幕上点击了一个按钮:
时间线 事件流转 ══════════════════════════════════════════════════════════════════ T0 手指触屏 电容屏感应 → 触摸IC → I2C/SPI → 内核驱动 T1 内核上报 input_event 写入 /dev/input/event2 T2 EventHub采集 epoll_wait 返回 → read() 读取 → 封装 RawEvent T3 InputReader加工 MultiTouchInputMapper 识别 DOWN 动作 → 坐标从绝对像素转为逻辑坐标 → 生成 NotifyMotionArgs(ACTION_DOWN, x, y) T4 InputDispatcher分发 查找触摸坐标命中的目标窗口 → 通过 InputChannel socket 发送 InputMessage T5 APP进程接收 NativeInputEventReceiver 从 socket 读到数据 → 转换为 MotionEvent 对象 → post 到主线程消息队列 T6 View树分发 ViewRootImpl → DecorView → ViewGroup → Button → Button.onTouchEvent(MotionEvent.ACTION_DOWN)整个过程在正常情况下仅需几毫秒到十几毫秒,这依赖于每一步的精巧设计:
- 内核驱动直接写入设备节点,零拷贝
- Native 层两个线程 PRIORITY_URGENT_DISPLAY 优先级
- socket pair 绕开 Binder 延迟
八、关键源码文件索引
Native 层(C++)
| 文件路径 | 说明 |
|---|---|
| frameworks/native/services/inputflinger/EventHub.cpp | 设备发现与原始事件读取 |
| frameworks/native/services/inputflinger/InputReader.cpp | 事件加工与 Mapper 调度 |
| frameworks/native/services/inputflinger/InputDispatcher.cpp | 事件分发与 ANR 监控 |
| frameworks/native/services/inputflinger/InputManager.cpp | 三剑客的启动与协调 |
| frameworks/native/services/inputflinger/InputListener.cpp | 监听器接口 |
| frameworks/native/libs/input/InputTransport.cpp | InputChannel 与 socket 通信 |
Java 管理层
| 文件路径 | 说明 |
|---|---|
| frameworks/base/services/core/java/com/android/server/input/InputManagerService.java | IMS 主类 |
| frameworks/base/services/core/java/com/android/server/input/InputMonitor.java | 输入监控器 |
| frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java | WMS,与 IMS 联动 |
JNI 桥接层
| 文件路径 | 说明 |
|---|---|
| frameworks/base/core/jni/android_view_InputEventReceiver.cpp | Native 事件接收器 |
| frameworks/base/core/jni/android_view_KeyEvent.cpp | KeyEvent JNI |
| frameworks/base/core/jni/android_view_MotionEvent.cpp | MotionEvent JNI |
应用层
| 文件路径 | 说明 |
|---|---|
| frameworks/base/core/java/android/view/ViewRootImpl.java | 窗口根,事件入口 |
| frameworks/base/core/java/android/view/InputEventReceiver.java | 应用侧事件接收器基类 |
| frameworks/base/core/java/android/view/View.java | View 基类,事件分发 |
| frameworks/base/core/java/android/view/ViewGroup.java | ViewGroup,事件拦截与分发 |
九、本系列预告
通过本篇的总览,你应该已经对 Android 7 输入系统的全貌有了清晰的认知。接下来的四篇将逐一深入:
- 第二篇:深入 EventHub,拆解 inotify + epoll 设备监听与 RawEvent 采集机制
- 第三篇:全面解析 InputReader 的 Mapper 体系,理解触摸、键盘、鼠标的事件加工原理
- 第四篇:剖析 InputDispatcher 的分派策略、socket 跨进程通信与 ANR 超时判定机制
- 第五篇:从 InputChannel 到 View.onTouchEvent(),理解应用侧事件消费的完整链路
敬请期待!
