Jimmy Chen

A Programmer

(转载)Input系统: InputManagerService的创建与启动

前言

在以前,我觉得 Input 系统没有什么值得研究的,就只是事件的传递过程而已。然而,在工作中,接触到越来越多的与 Input 系统相关的任务,掌握 Input 系统变得越来越重要,因此是时候全面分析一波 Input 系统。

Input 系统是由上层的 InputManagerSerice 启动的,而上层的 InputManagerService 只是 Input 系统一小部分,而主要的功能是集中在 native 层,因此 InuptManagerService 这个系统服务并不能代表整个 Input 系统。

Input系统的系列文章,是基于 Android 12 进行分析。

本文将以 IMS 简称 InputManagerService。

启动流程

InputManagerService 是一个系统服务,启动流程如下

IMS 的启动流程分为三步

  1. 创建输入系统,建立上层与底层的映射关系。
  2. 启动输入系统,其实就是启动底层输入系统的几个模块。
  3. 输入系统就绪,上层会同步一些配置给底层输入系统。

下面分三个模块,分别讲解这三步。

创建输入系统

IMS 构造函数,主要就是调用 nativeInit() 来初始化底层输入系统。

底层输入系统的初始化,其实就是创建 NativeInputManager,然后上层用 InputManagerService#mPtr 指向这个 NativeInputManager 对象。

NativeInputManager 的创建过程,其实初始化了底层的 Input 系统

NativeInputManager 构造过程如下

  1. 创建一个全局引用,并通过 NativeInputManager::mServiceObj 指向上层的 InputManagerService 对象。
  2. 初始化参数。这里要注意一个结构体变量 mLocked,它的一些参数都是由上层控制的。例如,mLocked.showTouches 是由开发者选项中 “Show taps” 决定的,它的功能是在屏幕上显示一个触摸点。
  3. 创建并注册服务 InputManager。

从这里,我们要明白几点事情

  1. 底层的 NativeInputManagerService 与上层的 InputManagerService 相互引用,这样它们就可以相互通信。
  2. InputManager 才是底层 Input 系统的真正的服务。

注意,创建 InputManager 使用了两个 this 参数,这里介绍下 NativeInputManager 和 InputManager 的结构图

`《(转载)Input系统: InputManagerService的创建与启动》

InputManager 构造函数需要的两个接口正好是由 NativeInputManager 实现的,然而,具体使用这两个接口的不是 InputManager,而是它的子模块。这些子模块都是在 InputManager 的构造函数中创建的

InputManager 构造函数所使用的两个接口,分别由 InputDispatcher 和 InputReader 所使用。因此 InputManager 向上通信的能力是由子模块 InputDispatcher 和 InputReader 实现的。

从这里可以看出,NativeInputManager 只是底层 Input 系统与上层的 Input 系统的桥梁而已

那么现在,我们可以推算出Input 系统的底层与上层的沟通方式

《(转载)Input系统: InputManagerService的创建与启动》

InputManager 创建了三个模块,InputReader、InputClassifier、InputDispatcher。 InputReader 负责从 EventHub 中获取事件,然后把事件加工后,发送给 InputClassfier。InputClassfier 会对事件进行分类,然后把事件发送给 InputDispatcher。最后 InputDispatcher 对进行事件分发给指定的窗口。基于这个理论,我们可以总结事件传递的流程,如下

《(转载)Input系统: InputManagerService的创建与启动》

EventHub 其实只属于 InputReader,因此要想解剖整个输入系统,我们得逐一解剖 InputReader、InputClassifier、InputDispatcher。后面的一系列的文章将逐个来剖析。

启动输入系统

输入系统的启动过程如下

  1. 启动底层输入系统。其实就是启动刚刚说到的 InputReader, InputDispatcher。
  2. 监听一些数据和广播,在合适的时机,更新配置给底层。
  3. 直接读取配置,更新到底层输入系统。

第2步和第3步,本质上其实都是更新配置到底层,但是要分析这个配置更新的过程,需要我们对 InputReader 的运行过程比较熟悉,因此这个配置更新过程,留到后面分析。

现在我们直接看下如何启动底层的输入系统

通过 JNI 层的 NativeInputManager 这个桥梁来启动 InputManager。

前面用一幅图表明了 NativeInputManager 的桥梁作用,现在感受到了吗?

InputManager 的启动过程很简单,就是直接启动它的子模块 InputDispatcher 和 InputReader。

InputDispatcher 和 InputReader 的启动,都是通过 InputThread 创建一个线程来执行任务。

注意 InputThread 可不是一个线程,InputThreadImpl 才是一个线程,如下

当线程启动后,会循环调用 threadLoop(),直到这个函数返回 false。从 InputThreadImpl 的定义可以看出,threadLoop() 会一直保持循环,并且每一次循环,会调用一次 mThreadLoop(),而函数 mThreadLoop 是由 InputReader 和 InputDispacher 在启动时传入

现在,我们可以明白,InputReader 启动时,会创建一个线程,然后循环调用 loopOnce() 函数,而 InputDispatcher 启动时,也会创建一个线程,然后循环调用 dispatchOnce()。

输入系统就绪

无论是通知底层加载键盘布局,还是加载设备别名,其实都是让底层更新配置。与前面一样,更新配置的过程,留到后面分析。

结束

通过本文,我们能大致掌握输入系统的轮廓。后面,我们将逐步分析子模块 InputReader 和 InputDispatcher 的功能。

本文转自 https://juejin.cn/post/7161376731096432653,如有侵权,请联系删除。

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注