Android输入系统(一)输入事件传递流程和InputManagerService的诞生

  • Android框架层
  • Android输入系统
  • Android框架层

本文首发于微信公众号「刘望舒」

前言

很多同学可能会认为输入系统是不是和View的事件分发有些关联,确实是有些关联,只不过View事件分发只能算是输入系统事件传递的一部分。这个系列讲的输入系统主要是我们不常接触的,但还是需要去了解的那部分。

1. 输入事件传递流程的组成部分

输入系统是外界与Android设备交互的基础,仅凭输入系统是无法完成输入事件传递的,因此需要输入系统和Android系统的其他成员来共同完成事件传递。输入系统事件传递需要经过以下几个部分。

输入事件传递流程可以大致的分为三个部分,分别是输入系统部分、WMS处理部分和View处理部分。下面分别对这几个部分进行简单的介绍。

输入系统部分

输入系统部分主要又分为输入子系统和InputManagerService组成(以下简称IMS),在Android中还有一个IMS(IP Multimedia Subsystem)意为为IP多媒体子系统,不要搞混了。 Android的输入设备有很多种,比如屏幕、键盘、鼠标、游戏手柄、操纵杆等等,其中应用开发接触最多的屏幕。当输入设备可用时,Linux内核会在/dev/input中创建对应的设备节点。 用户操作这些输入设备时会产生各种事件比如按键事件、触摸事件、鼠标事件等。 输入事件所产生的原始信息会被Linux内核中的输入子系统采集,原始信息由Kernel space的驱动层一直传递到User space的设备节点。

Android提供了getevent和sendevent两个工具帮助开发者从设备节点读取输入事件和写入输入事件。

IMS所做的工作就是监听/dev/input下的所有的设备节点,当设备节点有数据时会将数据进行加工处理并找到合适的Window,将输入事件派发给它。

WMS处理部分

Android解析WindowManagerService(一)WMS的诞生这篇文章中我讲过WMS的职责有四种,如下图所示。

WMS的职责之一就是输入系统的中转站,WMS作为Window的管理者,会配合IMS将输入事件交由合适的Window来处理。

View处理部分

View处理部分应该是大家最熟悉的了,一般情况下,输入事件最终会交由View来处理,应用开发者就可以通过一些回调方法轻松得到这个事件的封装类并对其进行处理,比如onTouchEvent(MotionEvent ev)方法。关于View体系可以查看View体系这一系列文章。

2. IMS的诞生

输入事件传递流程的组成部分我们已经了解了,本系列主要讲解输入系统部分中IMS对输入事件的处理,在这之前我们需要了解IMS的诞生。

2.1 SyetemServer处理部分

与AMS、WMS、PMS一样,IMS的在SyetemServer进程中被创建的,SyetemServer进程用来创建系统服务,不了解它的可以查看 Android系统启动流程(三)解析SyetemServer进程启动过程 这篇文章。 从SyetemServer的入口方法main方法开始讲起,如下所示。 frameworks/base/services/java/com/android/server/SystemServer.java

1public static void main(String[] args) { 2 new SystemServer().run(); 3 }

main方法中只调用了SystemServer的run方法,如下所示。 frameworks/base/services/java/com/android/server/SystemServer.java

1private void run() { 2 ... 3 try { 4 traceBeginAndSlog("StartServices"); 5 //启动引导服务 6 startBootstrapServices();//1 7 //启动核心服务 8 startCoreServices();//2 9 //启动其他服务 10 startOtherServices();//3 11 SystemServerInitThreadPool.shutdown(); 12 } catch (Throwable ex) { 13 Slog.e("System", "******************************************"); 14 Slog.e("System", "************ Failure starting system services", ex); 15 throw ex; 16 } finally { 17 traceEnd(); 18 } 19 ... 20}

在注释1中的startBootstrapServices方法中用SystemServiceManager启动了ActivityManagerService、PowerManagerService、PackageManagerService等服务。在注释2处的startCoreServices方法中则启动了DropBoxManagerService、BatteryService、UsageStatsService和WebViewUpdateService。注释3处的startOtherServices方法中启动了CameraService、AlarmManagerService、VrManagerService等服务。这些服务的父类均为SystemService。从注释1、2、3的方法可以看出,官方把系统服务分为了三种类型,分别是引导服务、核心服务和其他服务,其中其他服务是一些非紧要和一些不需要立即启动的服务。这些系统服务总共有100多个,我们熟知的AMS和PMS属于引导服务,WMS属于其他服务。 本文要讲的IMS属于其他服务,这里列出其他服务以及它们的作用,见下表。

其他服务作用
CameraService摄像头相关服务
AlarmManagerService全局定时器管理服务
InputManagerService管理输入事件
WindowManagerService窗口管理服务
VrManagerServiceVR模式管理服务
BluetoothService蓝牙管理服务
NotificationManagerService通知管理服务
DeviceStorageMonitorService存储相关管理服务
LocationManagerService定位管理服务
AudioService音频相关管理服务
.......

查看启动其他服务的注释3处的startOtherServices方法。 frameworks/base/services/java/com/android/server/SystemServer.java

1 private void startOtherServices() { 2 ... 3 inputManager = new InputManagerService(context);//1 4 traceEnd(); 5 traceBeginAndSlog("StartWindowManagerService"); 6 // WMS needs sensor service ready 7 ConcurrentUtils.waitForFutureNoInterrupt(mSensorServiceStart, START_SENSOR_SERVICE); 8 mSensorServiceStart = null; 9 wm = WindowManagerService.main(context, inputManager, 10 mFactoryTestMode != FactoryTest.FACTORY_TEST_LOW_LEVEL, 11 !mFirstBoot, mOnlyCore, new PhoneWindowManager());//2 12 ServiceManager.addService(Context.WINDOW_SERVICE, wm); 13 ServiceManager.addService(Context.INPUT_SERVICE, inputManager); 14 traceEnd(); 15 ... 16 17 }

注释1处创建了IMS,注释2处执行了WMS的main方法,其内部会创建WMS。需要注意的是,main方法的其中一个参数就是注释1处创建的IMS,在本地第1节中我们知道WMS是输入系统的中转站,其内部包含了IMS引用并不意外。紧接着将WMS和IMS添加到ServiceManager中进行统一的管理。

2.2 InputManagerService构造方法

我们接着来查看IMS的构造方法。 frameworks/base/services/core/java/com/android/server/input/InputManagerService.java

1public InputManagerService(Context context) { 2 this.mContext = context; 3 this.mHandler = new InputManagerHandler(DisplayThread.get().getLooper());//1 4 mUseDevInputEventForAudioJack = 5 context.getResources().getBoolean(R.bool.config_useDevInputEventForAudioJack); 6 Slog.i(TAG, "Initializing input manager, mUseDevInputEventForAudioJack=" 7 + mUseDevInputEventForAudioJack); 8 mPtr = nativeInit(this, mContext, mHandler.getLooper().getQueue());//2 9 10 ... 11 }

注释1处用android.display线程的Looper创建了InputManagerHandler,这样InputManagerHandler会运行在android.display线程,android.display线程是系统共享的单例前台线程,这个线程内部执行了WMS的创建,具体见 Android解析WindowManagerService(一)WMS的诞生这篇文章。 注释2处调用了nativeInit方法,很明显是要通过JNI调用Navive方法。 frameworks/base/services/core/jni/com_android_server_input_InputManagerService.cpp

1static jlong nativeInit(JNIEnv* env, jclass /* clazz */, 2 jobject serviceObj, jobject contextObj, jobject messageQueueObj) { 3 sp<MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj); 4 if (messageQueue == NULL) { 5 jniThrowRuntimeException(env, "MessageQueue is not initialized."); 6 return 0; 7 } 8 NativeInputManager* im = new NativeInputManager(contextObj, serviceObj, 9 messageQueue->getLooper());//1 10 im->incStrong(0); 11 return reinterpret_cast<jlong>(im); 12}

注释1处创建了NativeInputManager,最后会调用reinterpret_cast运算符将NativeInputManager指针强制转换并返回(重新解释比特位)。NativeInputManager的构造函数如下所示。 frameworks/base/services/core/jni/com_android_server_input_InputManagerService.cpp

1NativeInputManager::NativeInputManager(jobject contextObj, 2 jobject serviceObj, const sp<Looper>& looper) : 3 mLooper(looper), mInteractive(true) { 4 ... 5 sp<EventHub> eventHub = new EventHub(); 6 mInputManager = new InputManager(eventHub, this, this); 7}

NativeInputManager构造函数中创建了EventHub和InputManager,EventHub通过Linux内核的INotify与Epoll机制监听设备节点,通过EventHub的getEvent函数读取设备节点的增删事件和原始输入事件,本系列后续文章会详细介绍EventHub。InputManager的构造函数如下所示。 frameworks/native/services/inputflinger/InputManager.cpp

1InputManager::InputManager( 2 const sp<EventHubInterface>& eventHub, 3 const sp<InputReaderPolicyInterface>& readerPolicy, 4 const sp<InputDispatcherPolicyInterface>& dispatcherPolicy) { 5 mDispatcher = new InputDispatcher(dispatcherPolicy); 6 mReader = new InputReader(eventHub, readerPolicy, mDispatcher); 7 initialize(); 8} 9 10void InputManager::initialize() { 11 mReaderThread = new InputReaderThread(mReader); 12 mDispatcherThread = new InputDispatcherThread(mDispatcher); 13}

InputManager构造函数中创建了InputReader和InputDispatcher,InputReader会不断循环读取EventHub中的原始输入事件,将这些原始输入事件进行加工后交由InputDispatcher,InputDispatcher中保存了WMS中的所有Window信息(WMS会将窗口的信息实时的更新到InputDispatcher中),这样InputDispatcher就可以将输入事件派发给合适的Window。InputReader和InputDispatcher都是耗时操作,因此在initialize函数中创建了供它们运行的线程InputReaderThread和InputDispatcherThread。 InputManagerService构造方法描绘了如下的IMS简图。

从上面的简图可以看出来,IMS主要的工作都在Native层中,这些内容会在本系列的后续文章进行介绍。

感谢 《深入理解Android》卷3 《深入理解Android内核设计思想》

点赞
收藏

评论区

加载中...

相关推荐

MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1

文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s

Oracle 分组与拼接字符串同时使用

SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(

Android系统启动流程(四)Launcher启动过程与系统启动流程

Android框架层Android系统启动categories:Android框架层本文首发于微信公众号「刘望舒」前言此前的文章我们学习了init进程、Zygote进程和SyetemServer进程的启动过程,这一篇文章我们就来学习Android系统启动流程的最后一步:Launcher的启动流程,并结合本系列的前三

Android系统启动流程(一)解析init进程启动过程

作为“Android框架层”这个大系列中的第一个系列,我们首先要了解的是Android系统启动流程,在这个流程中会涉及到很多重要的知识点,这个系列我们就来一一讲解它们,这一篇我们就来学习init进程。"tag:Android框架层Android系统启动categories:Android框架层本文首发于微信公众号「刘望舒」

Android深入理解JNI(一)JNI原理与静态、动态注册

Android框架层Android深入理解JNIAndroid框架层本文首发于微信公众号「刘望舒」前言JNI不仅仅在NDK开发中应用,它更是Android系统中Java与Native交互的桥梁,不理解JNI的话,你就只能停留在JavaFramework层。这一个系列我们来一起深入学习JNI。<!more1.JNI概述Android系统按语言来划分的

Android输入系统(三)InputReader的加工类型和InputDispatcher的分发过程

Android框架层Android输入系统Android框架层本文首发于微信公众号「刘望舒」前言在上一篇文章中,我们学习了输入事件的处理,输入事件会交由InputDispatcher进行分发,那么InputDispatcher是如何进行分发的?这篇文章会给你答案。1.InputReader的加工类型在这篇文章中,我们知道InputReader会对原始