Input 是 Android 系统中的一个重要模块,它是负责处理用户输入操作的核心组件。该系统从各种输入设备(如触摸屏、键盘、鼠标等)获取原始输入事件,并将其转换为 Android 应用可以理解和消费的 KeyEvent 或 MotionEvent 对象。
一、Input简介
Input 系统对于提供流畅、灵敏和一致的用户交互体验至关重要,它需要在不同的设备、场景和应用中保证输入事件的正确性、及时性和安全性。
1、输入设备节点
在 Android 系统中,输入设备通过 /dev/input/ 目录下的节点进行通信。每个输入设备都会被分配一个 event 节点,例如 event0, event1、event2 等。这些节点允许用户空间的应用程序与硬件交互,接收来自键盘、鼠标、触摸屏等设备的输入事件。
可以通过 getevent -p 命令来查看所有连接的输入设备及其详细信息,包括支持的事件类型和值范围。也可以通过 /proc/bus/input/devices 文件查看类似信息。虽然具体的映射关系可能会根据设备制造商和配置有所不同,但常见的输入设备及其对应的event节点如下:
- 触摸屏:通常对应于某个 eventX 节点,具体取决于设备上安装的其他输入设备数量。可以通过检查设备名称来识别,如 "Synaptics Touch" 或类似的标识符。
- 键盘:同样会有一个 eventX 节点,对于外部键盘,它可能具有特定的供应商 ID 和产品 ID。
- 鼠标/触控板:这些也会有自己的 eventX 节点,并且可能同时支持按键和绝对坐标事件。
- 加速度计、陀螺仪等传感器:虽然严格来说不是 “输入” 设备,但在 Android 中它们也通过 input 子系统报告数据,因此会有相应的 eventX 节点。
二、关键类
1、InputManagerService
InputManagerService 是 Android 系统中负责管理输入设备和处理输入事件的核心服务之一。它主要工作在于初始化输入系统的各个组件、管理和监控输入设备,并为其他系统组件提供接口来查询和交互与输入相关的信息。
- 初始化输入系统:InputManagerService 负责启动并初始化 InputReader 和 InputDispatcher 这两个关键组件。InputReader 从输入设备读取原始数据并解析成高层次的输入事件,而 InputDispatcher 则将这些事件分发给当前激活的应用程序或系统服务。
- 设备管理:它能够检测新连接的输入设备(如触摸屏、键盘等),并通过 InputReader 加载相应的驱动程序和配置。同时,它也能识别设备断开的情况,提供 API 让其他系统组件可以查询已连接的输入设备列表及其属性(例如支持的按键类型、触控特性等)。
- 策略制定:InputManagerService 还负责实现一些高级的输入策略,比如屏幕旋转时如何调整输入坐标,或者在多窗口模式下如何正确地将输入事件分发到正确的应用界面。
- 与其他系统服务协作:与 WindowManager 紧密合作,确保输入事件被准确地发送到当前聚焦的窗口或视图。支持无障碍服务(Accessibility Services),允许这些服务监听或修改输入事件以辅助有特殊需求的用户。
- 安全性和权限控制:管理对输入设备访问的安全性,确保只有授权的应用程序才能接收特定类型的输入事件。
2、EventHub
在 Android 输入系统中,EventHub 是一个非常关键的组件,它位于输入系统的底层,负责直接与 Linux 内核提供的输入设备节点(如 /dev/input/eventX)进行交互。EventHub 从这些设备节点读取原始输入事件,并将它们传递给更高层的组件,例如 InputReader,以便进一步处理和分发。
主要职责
- 设备管理:EventHub 负责扫描 /dev/input 目录下的所有输入设备,并为每个设备打开相应的 event 节点。它可以检测到新设备的添加或现有设备的移除,并通知上层组件(如 InputManagerService)有关这些变化的信息。
- 事件读取:一旦打开了设备节点,EventHub 会持续监听来自这些设备的输入事件。使用非阻塞 I/O 操作高效地读取事件数据,确保即使在高负载情况下也能保持良好的响应性。
- 事件封装与转发:将从设备节点读取到的原始输入事件封装成 RawEvent 对象。这些 RawEvent 对象随后被传递给 InputReader,后者负责解析这些事件并生成高层次的输入事件(如 MotionEvent 或 KeyEvent),然后由 InputDispatcher 进行分发。
- 设备信息查询:提供接口让其他组件能够查询特定输入设备的详细信息,包括支持的事件类型、设备名称等。
工作流程
- 初始化阶段:当 InputManagerService 启动时,它会创建一个 EventHub 实例。EventHub 初始化过程中会打开所有的输入设备节点,并准备接收事件。
- 事件循环:EventHub 进入一个持续运行的循环,在这个循环中不断地检查是否有新的输入事件可用。如果有事件到达,则读取并将它们封装成 RawEvent 对象,然后通知 InputReader 有新的事件需要处理。
- 设备变更处理:如果检测到有新的输入设备连接或现有的设备断开,EventHub 会通知 InputManagerService,从而允许系统动态调整对新设备的支持或移除旧设备的引用。
因此,EventHub 提供了与底层硬件交互的基础,确保了各种输入设备产生的事件能够被正确地捕获、解析并最终传递给应用程序。
3、InputReader
在 Android 输入系统中,InputReader 是一个核心组件,负责从底层硬件读取原始输入事件,如通过调用 EventHub 的接口,管理设备的添加与删除,并进行事件的加工处理。其内部会不断的通过 EventHub 的 getEvents 轮询驱动的事件,并最终将事件发送给 InputDispatcher 进行派发。并将这些事件解析为更高层次的事件类型(如触摸事件 MotionEvent 和按键事件 KeyEvent),以便上层的应用程序或服务能够理解和处理这些输入。
主要职责
- 事件解析:从 EventHub 接收原始输入事件(通过读取 /dev/input/eventX 节点获取)。解析这些原始事件为高层次的输入事件类型,例如:触摸屏输入 -> MotionEvent、键盘输入 -> KeyEvent、鼠标或其他指针设备输入 -> MotionEvent 等。
- 设备支持:支持多种类型的输入设备,包括但不限于触摸屏、键盘、鼠标等。根据设备的不同特性,正确地解释和转换输入数据。例如,触摸屏可能需要特别处理多点触控或多手势操作。
- 状态管理:维护当前的输入状态,比如跟踪触摸点的位置、按键的状态等。处理输入事件的时间戳,确保事件按正确的顺序被分发。
- 与 InputDispatcher 协作:将解析后的事件传递给InputDispatcher,由它决定如何将这些事件分发给前台应用程序或系统服务进行处理。
工作流程
- 初始化阶段:当 InputManagerService 启动时,它会创建一个 InputReader 实例。InputReader 初始化过程中会准备读取来自所有连接的输入设备的数据。
- 事件循环:InputReader 进入一个持续运行的循环,在这个循环中不断地从 EventHub 获取新的原始输入事件。对于每个获取到的事件,InputReader 都会根据事件类型和来源设备对其进行解析。
- 事件分发:解析完成后,InputReader 生成相应的高层次事件对象(如 MotionEvent 或 KeyEvent)。这些事件随后被传递给 InputDispatcher,后者根据当前的焦点窗口和其他因素决定哪个应用或服务应该接收这些事件。
总之,InputReader 主要负责将来自各种输入设备的原始事件转换为应用程序可以直接使用的高层次事件。
4、InputDispatcher
InputDispatcher 是 Android 输入系统中的一个关键组件,它负责接收由 InputReader 解析出来的高层次输入事件(如触摸事件 MotionEvent 和按键事件 KeyEvent),并将这些事件分发到当前活动的应用程序或系统服务中进行处理。InputDispatcher 确保了用户交互能够被正确且高效地传递给前台应用或适当的后台服务。
主要职责
- 事件分发:接收来自 InputReader 的输入事件,并根据当前的焦点窗口和视图层次结构将事件分发给正确的应用程序或系统服务。处理多任务环境下的事件分发,确保即使在多窗口或多应用切换的情况下也能准确地将事件发送到正确的接收者。
- 策略实施:实施一系列策略来决定如何处理特定类型的输入事件。例如,处理屏幕旋转时的坐标转换、管理输入法(IME)与应用之间的交互等。支持无障碍服务(Accessibility Services),允许这些服务监听或修改输入事件以辅助有特殊需求的用户。
- 同步与异步处理:通过使用同步屏障(Synchronous Barrier)机制来确保某些关键输入事件(如触摸事件)在适当的时间点被处理,避免因其他高优先级任务导致的延迟。提供异步事件处理能力,使得一些非关键的输入事件可以在不影响用户体验的情况下稍后处理。
- 状态跟踪:维护输入通道的状态信息,包括当前激活的窗口、视图层级关系以及各个输入设备的状态等。监控输入设备的变化(如设备添加或移除),并相应地调整事件分发策略。
工作流程
- 初始化阶段:当 InputManagerService 启动时,会创建一个 InputDispatcher 实例。初始化过程中,InputDispatcher 会准备接收来自 InputReader 的输入事件,并设置必要的回调接口用于事件分发。
- 事件循环:InputDispatcher 进入一个持续运行的循环,在这个循环中等待新的输入事件到来。每当接收到一个新的输入事件时,InputDispatcher 会基于当前的窗口焦点和视图层次结构确定哪个应用或服务应该接收该事件。
- 事件分发:根据确定的目标应用或服务,InputDispatcher 将事件通过 InputChannel 发送到相应的接收者。如果目标应用正在处理其他高优先级任务,InputDispatcher 可能会选择暂时缓存事件,直到应用准备好接收为止。
InputDispatcher 主要是确保了用户的输入能够被正确且及时地传递给前台应用或适当的后台服务。
5、WindowManagerService
WindowManagerService(简称 WMS)是 Android 系统中的核心服务之一,负责管理系统中所有的窗口。它管理着窗口的创建、销毁、布局、显示顺序以及输入事件的分发等任务。WMS 与 InputDispatcher 和 InputReader 密切合作,确保用户交互能够被正确地传递给相应的应用程序或系统服务。
主要职责
- 窗口管理:负责管理所有窗口的生命周期,包括窗口的添加、删除和更新。维护窗口的层级结构,决定哪些窗口应该在前面显示,哪些应该在后面。
- 布局与绘制:计算每个窗口的大小和位置,并告知 SurfaceFlinger 如何合成这些窗口以生成最终的屏幕图像。处理多窗口模式(如分屏模式),调整各应用窗口的布局以适应不同的显示需求。
- 输入事件分发:与 InputDispatcher 协同工作,确定哪个窗口应该接收特定的输入事件(如触摸或按键事件)。根据当前窗口的焦点状态和视图层次结构,将输入事件准确地分发到正确的应用或服务。
- 屏幕方向与动画:管理屏幕的方向变化,并协调相关窗口的重新布局和重绘。支持窗口转换动画,提升用户体验。
- 多显示器支持:在支持多显示器的设备上,WMS 可以将不同窗口分配到不同的显示器上显示。
工作流程
- 初始化阶段:当 Android 系统启动时,SystemServer 进程会启动 WMS。初始化过程中,WMS 会准备接收来自客户端(如 AMS)的窗口请求,并设置必要的回调接口用于处理这些请求。
- 窗口操作:应用程序通过调用 WindowManager 的 API 来请求创建、更新或删除窗口。
- WMS 接收到这些请求后,根据当前的窗口状态和布局规则进行相应的处理,并通知 SurfaceFlinger 更新显示内容。
- 输入事件处理:输入事件首先由 InputReader 解析为高层次的事件类型,然后传递给 InputDispatcher。InputDispatcher 使用 WMS 提供的信息来决定哪个窗口应接收该事件,并通过窗口关联的 InputChannel 将事件发送给目标应用。
- 屏幕方向与动画:当检测到设备旋转或其他需要改变屏幕方向的情况时,WMS 会协调所有受影响的窗口进行重新布局,并触发相应的动画效果。
WMS 仅管理着系统中所有窗口的生命周期和布局,还负责确保用户交互能够被正确地传递给前台应用或适当的后台服务。WMS 在每次新建窗口时,都会将窗口信息更新到 InputDispatcher 中,这样 InputDispatcher 才能找到对应的窗口。
6、ViewRootImpl
ViewRootImpl 是 Android 系统中连接 WindowManager 和 View 层次结构的关键组件。它负责管理视图树的测量、布局、绘制过程,并处理输入事件的接收和分发。作为每个窗口与系统服务交互的桥梁,ViewRootImpl 在 Android 应用的 UI 渲染和用户交互方面扮演着至关重要的角色。
主要职责
- UI 渲染:负责视图树的测量(measure)、布局(layout)和绘制(draw)流程。通过调用 performTraversals() 方法来触发这些过程,确保视图能够正确地显示在屏幕上。
- 输入事件处理:接收来自 InputDispatcher 的输入事件(如触摸或按键事件),并通过 dispatchInputEvent() 方法将这些事件分发给适当的视图进行处理。支持事件预处理(如手势识别)和后处理(如滚动、缩放等)。
- 与 WMS 交互:作为应用端与 WMS 通信的接口,负责向 WMS 注册窗口,并接收有关窗口状态变化的通知(如屏幕旋转、尺寸变化等)。处理窗口属性的更新请求,如调整大小、位置或透明度等。
- 动画支持:支持视图层次结构中的动画效果,包括属性动画、过渡动画等。协调视图的重绘以确保动画流畅运行。
- 硬件加速:如果启用了硬件加速,ViewRootImpl 将使用 OpenGL ES 来渲染视图,从而提高性能和视觉效果。
工作流程
- 初始化阶段:当一个新的窗口被创建时,系统会为该窗口创建一个 ViewRootImpl 实例。初始化过程中,ViewRootImpl 会设置与之关联的 DecorView(顶级视图容器),并准备开始处理视图的生命周期事件。
- 视图绘制:ViewRootImpl 调用 performTraversals() 方法来启动视图的测量、布局和绘制流程。这个方法会在需要时递归遍历整个视图树,确保每个视图都被正确地测量和布置。
- 输入事件处理:输入事件首先由 InputReader 解析,然后通过 InputDispatcher 分发到正确的 ViewRootImpl 实例。ViewRootImpl 使用 WindowInputEventReceiver 来接收这些事件,并通过 dispatchInputEvent() 方法将其传递给相应的视图进行处理。
- 与 WMS 交互:当应用程序请求更改窗口属性时,ViewRootImpl 会与 WMS 通信,请求相应的变更。同样,当 WMS 发送有关窗口状态变化的通知时,ViewRootImpl 会相应地更新其内部状态,并通知相关视图进行必要的调整。
ViewRootImpl 是 Android 应用程序中连接视图层次结构与系统服务的核心组件,负责管理视图的渲染和输入事件的处理。
总结
Android 输入系统是一个复杂但高效的设计,旨在处理来自各种输入设备(如触摸屏、键盘等)的用户交互,并将这些交互转换为应用程序可以理解和响应的形式。
- EventHub:输入系统的最底层,直接与硬件通信,监听和读取输入设备的原始事件。
- InputReader:从 EventHub 获取原始输入事件,对其进行解析,转化为更高层次的事件,以便上层应用或服务能够理解。
- InputDispatcher:接收由 InputReader 转换后的高层次输入事件,并基于当前窗口焦点状态,通过 InputChannel 将事件分发到正确的应用程序或服务中。
- IMS:管理着 EventHub、InputReader 和 InputDispatcher 的生命周期,同时也提供了对外的接口,使得其他系统组件可以查询输入设备的信息。
- WMS:负责管理窗口的层级结构和布局,确定哪个窗口应该接收到输入事件,并将这些信息传递给 InputDispatcher,以便准确地分发事件。
- ViewRootImpl:是每个窗口的根视图实现类,它不仅负责视图的测量、布局和绘制,还通过 InputChannel:接收来自 InputDispatcher 的输入事件,并将这些事件进一步分发给具体的视图组件进行处理。
因此,在 Android 输入系统中,EventHub 担任了与硬件交互的角色,而 InputReader 则负责将这些原始数据解析成可理解的事件类型。InputDispatcher 根据 WMS 提供的信息,将这些事件分发给合适的窗口。IMS 作为一个管理者,确保整个输入流程的顺利进行。最后,ViewRootImpl 在每个窗口内扮演着重要角色,负责视图的具体渲染和事件处理,确保用户交互能够被正确地解释和响应。这些组件紧密合作,共同构成了一个强大且灵活的输入系统,支持多种类型的输入设备和复杂的用户界面需求。