参考深入理解Android卷3
本文将详细讨论Android输入系统的工作原理,包括输入设备的管理,输入事件的加工方式以及派发流程。 重点讨论输入设备和输入事件
监控/dev/input/下所有的设备节点,当某个节点有数据可读时,将数据读出来进行一系列的翻译加工,然后在所有的窗口中寻找合适的事件接收者,并派发给他。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-StsrdOgl-1585907160596)(https://wizardforcel.gitbooks.io/deepin-android-vol3/content/img/5-1.png)] 输入系统的总体流程和参与者图 图中描述了整个输入流程的参与者。
Linux内核,接收输入设备的中断,并将原始事件的数据写入设备节点中设备节点,作为内核和IMS的桥梁,他将原始事件的数据暴露给用户空间,以便IMS可以从中读取事件InputManagerService,Android系统服务,分java和native两层,java层与wms通信,native层则是InputReader和InputDispather两个输入系统关键组件的运行容器EventHub,直接访问所有的设备节点,并且正如起名字所描述,他通过getEvents()的函数将所有输入系统相关的待处理底层事件返回给使用者。事件包括原始输入事件和设备节点的增删等InputReader,是IMS的关键组件之一。独立运行于一个线程,负责管理输入设备的列表和配置,以及进行输入事件的加工和处理。它通过起线程循环不断地通过getEvents()函数从EventHub中将事件读取出来并进行处理。对于设备节点的增删事件,他会更新输入列表与配置。对于原始事件,InputReader对其进行翻译、组装、封装为包含更多信息、更具可读性的输入事件、然后交给InputDispatcher进行派发InputReaderPolicy,他作为InputReader的事件加工处理提供一些策略配置,例如键盘布局等信息InputDispatcher 是IMS的另一个关键组件。也独立运行于一个线程,其中保存了来自WMS的所有窗口的信息,其收到来自InputReader的输入事件后,会在其保存的窗口中寻找合适的窗口,并将事件派发到此窗口InputDispatcherPolicy 他为InputDispatcher派发提供策咯控制。例如截取某些特定的输入事件用于特殊用途,或者阻止将某些事件派发给目标窗口。一个典型的例子就是Home键被其截取到PhoneWindowManager中进行处理,并阻止窗口收到home键按下的事件。WMS 它不是输入系统中的一员,但是它却对InputDIspatcher的正常工作提供重要的作用。当新建窗口时,WMS为新窗口和IMS创建了事件传递所用的通道。另外,WMS还将所有的窗口信息,包括窗口的可点击区域、焦点窗口等信息、实时更新到IMS的InputDIspatcher中,使得InputDispatcher可以正确的将事件发到指定的窗口ViewRootImpl 对某些窗口的,如壁纸,SurfaceView的窗口来说,窗口就是输入事件的终点。而对于其他的如activity,对话框等使用了Android控件系统的窗口来说,输入事件的终点就是控件(View)。ViewRootImpl将窗口所接收的事件沿着控件树间事件派发到感兴趣的控件。总得来说,内核将原始事件写入设备节点中,InputReader不断地通过EventHub将原始事件取出来并翻译加工成Android输入事件,然后交给InputDispatcher。InputDispatcher根据WMS提供的窗口信息将事件交给合适的窗口。窗口的ViewRootImpl对象沿着控件树将事件派发给感兴趣的控件。控件对其收到的事件作出响应,更新自己的动画、执行特定的动作。
通过IMS启动的过程探讨IMS的构成。IMS分为Java和Native层两个部分,其启动过程是从Java部分的初始化开始,进而完成Native部分的初始化
IMS的诞生分为两个阶段:
创建新的IMS对象调用IMS对象的start()函数完成启动IMS的构造函数
[InputManagerService.java --> InputManagerService.InputManagerService()] public InputManagerService(Context context) { this.mContext = context; /*使用DisplayThread的looper创建一个handler,该handler实际运行在DisplayThread线程中*/ this.mHandler = new InputManagerHandler(DisplayThread.get().getLooper()); mUseDevInputEventForAudioJack = context.getResources().getBoolean(R.bool.config_useDevInputEventForAudioJack); Slog.i(TAG, "Initializing input manager, mUseDevInputEventForAudioJack=" + mUseDevInputEventForAudioJack); /*每一个分为Java和Native两部分的对象在创建时,都会有一个nativeInit函数*/ mPtr = nativeInit(this, mContext, mHandler.getLooper().getQueue()); String doubleTouchGestureEnablePath = context.getResources().getString( R.string.config_doubleTouchGestureEnableFile); mDoubleTouchGestureEnableFile = TextUtils.isEmpty(doubleTouchGestureEnablePath) ? null : new File(doubleTouchGestureEnablePath); /*将本地service添加到全局Service.arraylist中,方便其他线程调用*/ LocalServices.addService(InputManagerInternal.class, new LocalService()); }可以看到IMS的构造函数很简单。大部分初始化工作都放在Native层。
[com_android_server_input_InputManagerService.cpp-->nativeInit] static jlong nativeInit(JNIEnv* env, jclass /* clazz */, jobject serviceObj, jobject contextObj, jobject messageQueueObj) { sp<MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj); if (messageQueue == NULL) { jniThrowRuntimeException(env, "MessageQueue is not initialized."); return 0; } /*创建一个NativeInputManager对象,此对象是是native层和Java层沟通的桥梁*/ NativeInputManager* im = new NativeInputManager(contextObj, serviceObj, messageQueue->getLooper()); im->incStrong(0); /*返回NativeInputManager对象的指针给java层的IMS,IMS保存在mPtr成员变量中*/ return reinterpret_cast<jlong>(im); } [com_android_server_input_InputManagerService.cpp ] class NativeInputManager : public virtual RefBase, public virtual InputReaderPolicyInterface, public virtual InputDispatcherPolicyInterface, public virtual PointerControllerPolicyInterface { }看这个类的声明可以发现,他实现了InputReaderPolicyInterface和InputDispatcherPolicyInterface两个接口。 NativeInputManager为两个策略提供接口实现而已,而不是策略的实际实现者。NativeInputManager通过JNI回调Java层的IMS,由它完成决策。
[com_android_server_input_InputManagerService.cpp-->NativeInputManager::NativeInputManager()] NativeInputManager::NativeInputManager(jobject contextObj, jobject serviceObj, const sp<Looper>& looper) : mLooper(looper), mInteractive(true) { JNIEnv* env = jniEnv(); mContextObj = env->NewGlobalRef(contextObj); mServiceObj = env->NewGlobalRef(serviceObj); { AutoMutex _l(mLock); mLocked.systemUiVisibility = ASYSTEM_UI_VISIBILITY_STATUS_BAR_VISIBLE; mLocked.pointerSpeed = 0; mLocked.pointerGesturesEnabled = true; mLocked.showTouches = false; } mInteractive = true; /* 创建了EventHub*/ sp<EventHub> eventHub = new EventHub(); /*创建Native层的inputmanager*/ mInputManager = new InputManager(eventHub, this, this); }在NativeInputManager的构造函数中,创建了两个关键角色,EventHub和InputManager。本节重点关注InputManager,EventHub下部分重点分析。 下面来看InputManager的构造函数
[InputManager.cpp] InputManager::InputManager( const sp<EventHubInterface>& eventHub, const sp<InputReaderPolicyInterface>& readerPolicy, const sp<InputDispatcherPolicyInterface>& dispatcherPolicy) { /*创建InputDispatcher*/ mDispatcher = new InputDispatcher(dispatcherPolicy); /*InputReader*/ mReader = new InputReader(eventHub, readerPolicy, mDispatcher); //初始化 initialize(); }在来看initialize();
[InputManager.cpp] void InputManager::initialize() { //创建供InputReader运行的线程InputReaderThread mReaderThread = new InputReaderThread(mReader); //创建供InputDispatcher运行的线程InputDispatcherThread mDispatcherThread = new InputDispatcherThread(mDispatcher); }InputManager的构造函数也比较简洁。创建了4个对象,分别为IMS的核心参与者InputReader和InputDispatcher,以及他们所在的线程InputReaderThread和InputDispatcherThread。注意inputmanager的构造函数参数ReaderPolicy和DispatcherPolicy都是NativeInputManager。 至此,IMS的创建已经完成。在这个过程中,输入系统的重要参与者都完成了创建,并得到了如图的一套体系。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yuFtPAG0-1585907160597)(https://wizardforcel.gitbooks.io/deepin-android-vol3/content/img/5-2.png)] IMS的结构体系
完成了IMS的创建后,执行了IMS.start()启动IMS。 InputManager的创建过程分别为InputReader与InputDispatcher创建了承载他们的线程,但并未启动。此时的start()函数的功能就是启动这两个线程,使得InputReader和InputDispatcher开始工作。
当两个线程启动后,InputReader在其线程循环中不断地从EventHub中抽取原始输入事件,进行加工处理后将加工所得的事件放入InputDispatcher的派发发队列中。InputDispatcher则在其线程循环中将派发队列中的事件取出,查找合适的窗口,将事件写入到窗口的事件接收管道中。窗口事件接收线程的Looper从管道中将事件取出,交由事件处理函数进行事件响应。整个过程共有三个线程首尾相接,像三台水泵似的一层层地将事件交付给事件处理函数 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IxGxGJ6o-1585907160598)(https://wizardforcel.gitbooks.io/deepin-android-vol3/content/img/5-3.png)]
InputManagerService.start()函数的作用,就像为Reader线程、Dispatcher线程这两台水泵按下开关,而Looper这台水泵在窗口创建时便已经处于运行状态了。自此,输入系统动力十足地开始运转,设备节点中的输入事件将被源源不断地抽取给事件处理者。
IMS的成员关系 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LYe9eiwg-1585907160599)(https://wizardforcel.gitbooks.io/deepin-android-vol3/content/img/5-4.png)]