深入理解Android输入系统--初识Android输入系统
来源:互联网 发布:分布式数据存储 编辑:程序博客网 时间:2024/05/16 01:24
初始Android输入系统
参考深入理解Android卷3
本文将详细讨论Android输入系统的工作原理,包括输入设备的管理,输入事件的加工方式以及派发流程。
重点讨论输入设备和输入事件
Android输入系统的工作原理:
监控/dev/input/下所有的设备节点,当某个节点有数据可读时,将数据读出来进行一系列的翻译加工,然后在所有的窗口中寻找合适的事件接收者,并派发给他。
输入系统简介
输入系统的总体流程和参与者图
图中描述了整个输入流程的参与者。
- 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的构成。IMS分为Java和Native层两个部分,其启动过程是从Java部分的初始化开始,进而完成Native部分的初始化
IMS的诞生
[SystemServer.java-->startOtherServices()]private void startOtherServices() { InputManagerService inputManager = null; ... /* 创建inputmananger对象*/ inputManager = new InputManagerService(context); 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); inputManager.setWindowManagerCallbacks(wm.getInputMonitor()); /*启动IMS */ inputManager.start(); ...}
IMS的诞生分为两个阶段:
- 创建新的IMS对象
- 调用IMS对象的start()函数完成启动
IMS的创建
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的创建已经完成。在这个过程中,输入系统的重要参与者都完成了创建,并得到了如图的一套体系。
IMS的结构体系
IMS的启动和运行
完成了IMS的创建后,执行了IMS.start()启动IMS。
InputManager的创建过程分别为InputReader与InputDispatcher创建了承载他们的线程,但并未启动。此时的start()函数的功能就是启动这两个线程,使得InputReader和InputDispatcher开始工作。
当两个线程启动后,InputReader在其线程循环中不断地从EventHub中抽取原始输入事件,进行加工处理后将加工所得的事件放入InputDispatcher的派发发队列中。InputDispatcher则在其线程循环中将派发队列中的事件取出,查找合适的窗口,将事件写入到窗口的事件接收管道中。窗口事件接收线程的Looper从管道中将事件取出,交由事件处理函数进行事件响应。整个过程共有三个线程首尾相接,像三台水泵似的一层层地将事件交付给事件处理函数
InputManagerService.start()函数的作用,就像为Reader线程、Dispatcher线程这两台水泵按下开关,而Looper这台水泵在窗口创建时便已经处于运行状态了。自此,输入系统动力十足地开始运转,设备节点中的输入事件将被源源不断地抽取给事件处理者。
IMS的成员关系
IMS的成员关系
- 深入理解Android输入系统--初识Android输入系统
- 深入理解Android输入系统
- 第5章 深入理解Android输入系统(节选)
- Android系统级深入开发输入系统
- 《深入理解Android 卷III》第五章 深入理解Android输入系统
- 深入理解Android卷III 第5章 深入理解Android输入系统 (节选)
- 《深入理解Android 卷III》第五章 深入理解Android输入系统
- 《深入理解Android 卷III》第五章 深入理解Android输入系统
- 跟着Innost理解下Android输入系统
- Android触摸屏输入系统
- Android触摸屏输入系统
- Android 输入系统
- Android触摸屏输入系统
- Android触摸屏输入系统
- Android触摸屏输入系统
- Android 输入系统
- Android输入系统
- Android输入系统
- Epoll介绍和使用
- IntelliJ IDEA快捷键
- java.util.HashMap<K,V>
- Nginx上的第一个HelloWorld
- C# EF Core 简单工厂模式,接口多继承实例(三)
- 深入理解Android输入系统--初识Android输入系统
- LEETCODE 153. Find Minimum in Rotated Sorted Array
- 关于空指针报错的问题
- leetcode hard模式专杀之52 N-Queens II
- HDU 2255 (2/600)
- TCP协议三次握手过程分析
- Perl面向对象编程入门
- CentOS7下curl使用
- javascript中原型的理解(1)