参考:http://blog.csdn.net/u010312937/article/details/53285286
https://www.jianshu.com/p/7fca94b330ea
注意:关于下面的一些时序图,如果看不清,可以使用下载后再看,源码我已经放在GitHub上,工具为bouml,
GitHub地址为:https://github.com/maogefff/Other/tree/master
也可以使用我将时序图转化为PNG图片以后的,GitHub地址为:https://github.com/maogefff/Other/tree/inputSystem
1. 框架
InputManagerService(IMS)
Linux内核,接受输入设备的中断,并将原始事件的数据写入设备节点中,设备节点作为内核与IMS的桥梁,将原始事件的数据暴露给用户空间,以便IMS可以从中读取事件
InputManagerService,一个android系统服务,分为Java层和Native层两部分,java层负责与WMS通信,而Native层则是InputReader和InputDispatcher两个输入系统关键组件的运行容器
EventHub,直接访问所有的设备节点,通过一个名为getEvents()的函数将所有输入系统相关的待处理的底层事件返回给使用者,包括原始输入事件,设备节点的增删等
InputReader,是IMS中的关键组件之一,它运行一个独立的线程中,负责管理输入设备的列表和配置,以及进行输入事件的加工处理,它通过其线程循环不断地通过getEvents()函数从EventHub中将事件取出并进行处理,对于设备节点的增删事件,它会更新输入设备列表与配置,对于原始输入事件,InputReader对其进行翻译,组装,封装为包含更多信息,更多可读性的输入事件,然后交给InputDispatcher进行派发
InputReaderPolicy,为InputReader的事件加工处理提供一些策略配置,例如键盘布局信息等。
InputDispatcher,是IMS中的另一个关键组件,运行于一个独立的线程中,InputDispatcher中保管来自WMS的所有窗口的信息,收到InputReader的输入事件后,会在其保管的窗口中寻找合适的窗口,并将事件派发给此窗口
InputDispatcherPolicy,为InputDispatcher的派发过程提供策略控制,例如HOME键被InputDispatcherPolicy截取到PhoneWindowManager中处理,并阻止窗口收到HOME键按下的事件
WMS,并不是输入系统的一员,新建窗口时,WMS为新窗口和IMS创建了事件传递所用的通道,会将窗口的可点击区域,焦点窗口等信息实时更新到IMS的InputDispatcher中,使得InputDispatcher可以正确将事件派发到指定窗口
ViewRootImpl,对某些窗口,如壁纸窗口,SurfaceView的窗口来说,窗口就是输入事件派发的终点,而对其他的如Activity,对话框等使用了Android控件系统的窗口来说,输入事件的终点是控件
运行流程
简单来说,内核将原始事件写入到设备节点中,InputReader不断地通过EventHub将原始事件取出来并翻译加工成Android输入事件,然后交给InputDispatcher。InputDispatcher根据WMS提供的窗口信息将事件交给合适的窗口。窗口的ViewRootImpl对象再沿着控件树将事件派发给感兴趣的控件。控件对其收到的事件作出响应,更新自己的画面、执行特定的动作。所有这些参与者以IMS为核心,构建了Android庞大而复杂的输入体系。
2. 阅读代码后的总结
2.1 启动创建流程
图2.1非常重要;就是Android层输入子系统的创建流程,看不清可以github上下载下来看,黄色为JAVA代码,红色为CPP代码,蓝色为CPP代码所在文件
大致思路:
SystemServer创建整个输入子系统InputManagerService类用来管理整个输入子系统 (JAVA层)
InputManagerService调用构造函数然后调用nativeInit(调用到C++层)创建本地管理者NativeInputManager,然后创建各种累
下面有三个重要的功能类(这三个功能详看第一节)
EventHub: 实例化对象为eventHub
InputReader: 实例化对象为mReader
InputDispatcher:实例化对象为mDispatcher
还有三个重要的类:
InputManager: 实例化对象为minputManager ;主要作用为管理下面两个线程,mReaderThread和mDispathcerThread
InputReaderThread: 实例化对象为mReaderThread ;为一个线程的对象,主要作用为管理mReader
InputDispatcherThread: 实例化对象为mDispathcerThread ;为一个线程的对象,主要作用为管理mDispatcher
图2.1 创建输入系统时序图
图2.2 类之间的包含关系
2.2 InputReaderThread和EventHub的流程
(一). InputReaderThread流程:
InputReader如下图2.3所示,主要负责三个方面的工作:
1. 获得事件(事件是通过EventHub获取,包括:1. 设备节点的增删事件; 2. 原始输入事件)
2. 对事件进行简单的处理(1. 对于设备节点的增删事件(更新设备列表和配置); 2. 原始输入事件(进行翻译和组装))
3. 将处理后的事件分发给dispatch
(二). EventHub流程:
EventHub如2.3所示,主要负责检测设备节点增删事件和原始输入事件
通过inotify检测/dev/input文件夹是否有节点增删,通过epoll分别检测/dev/input/xxx里面的内容是否有修改
1. 通过epoll_wait等待获取事件(无事件等待)
2. 获取增删事件后
3. 获取原始输入事件后
下面是伪代码:
getEvents(RawEvent* buffer) { //检测目录是否有增删 fd1 = inotify.init("/dev/input"); //打开具体设备节点 fd2 = open("/dev/input/event0"); fd3 = open("/dev/input/event1"); //同时检测文件和文件夹 int pollResult = epoll_wait(fd1, fd2, fd3); switch(){ case 增加了一个设备节点: buffer.type = DEVICE_ADDED; fd4 = open("dev/input/xxx"); epoll_wait(fd1, fd2, fd3, fd4); //在epoll_wait里再增加一个fd4 break; case 减少了一个设备节点: buffer.type = DEVICE_REMOVE; epoll_wait(fd1, fd2, fd3); //在epoll_wait里再删除掉fd4 break; case 文件内容修改了: buffer.xxx = xxx; //构造RawEvent break; } }
黄色代码类名,蓝色代表所在文件(注意:时序图在EventHub.cpp当中有点问题,loop不是包含第三个,而是包含整个)
图2.3 readThread时序图
3. InputManagerService(IMS)流程分析
3.1 启动流程:
frameworks/base/services/java/com/android/server/SystemServer.java
public static void main(String[] args) { new SystemServer().run(); } ... private void run() { ...
// Start services. try { startBootstrapServices(); startCoreServices(); startOtherServices(); //其他服务里面 } catch (Throwable ex) { Slog.e("System", "******************************************"); Slog.e("System", "************ Failure starting system services", ex); throw ex; } ... // Loop forever. Looper.loop(); throw new RuntimeException("Main thread loop unexpectedly exited"); }
private void startOtherServices() { ... NsdService serviceDiscovery= null; WindowManagerService wm = null; //窗口管理服务 ... InputManagerService inputManager = null; //输入管理服务 ... Slog.i(TAG, "Input Manager"); //创建输入系统 inputManager = new InputManagerService(context); Slog.i(TAG, "Window Manager"); wm = WindowManagerService.main(context, inputManager, mFactoryTestMode != FactoryTest.FACTORY_TEST_LOW_LEVEL, !mFirstBoot, mOnlyCore); ServiceManager.addService(Context.WINDOW_SERVICE, wm); //将输入系统加入系统服务当中,以便其他人可以方便的访问(包括应用程序) ServiceManager.addService(Context.INPUT_SERVICE, inputManager); mActivityManagerService.setWindowManager(wm); //设置输入系统的回调函数:设置向WMS发起回调 inputManager.setWindowManagerCallbacks(wm.getInputMonitor()); //启动输入系统服务 inputManager.start(); ... }