初始Android输入系统
本文将详细讨论Android输入系统的工作原理,包括输入设备的管理,输入事件的加工方式以及派发流程。
重点讨论输入设备和输入事件
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的构成。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的创建已经完成。在这个过程中,输入系统的重要参与者都完成了创建,并得到了如图的一套体系。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yuFtPAG0-1585907160597)(https://wizardforcel.gitbooks.io/deepin-android-vol3/content/img/5-2.png)]
IMS的结构体系
IMS的启动和运行
完成了IMS的创建后,执行了IMS.start()启动IMS。
InputManager的创建过程分别为InputReader与InputDispatcher创建了承载他们的线程,但并未启动。此时的start()函数的功能就是启动这两个线程,使得InputReader和InputDispatcher开始工作。
当两个线程启动后,InputReader在其线程循环中不断地从EventHub中抽取原始输入事件,进行加工处理后将加工所得的事件放入InputDispatcher的派发发队列中。InputDispatcher则在其线程循环中将派发队列中的事件取出,查找合适的窗口,将事件写入到窗口的事件接收管道中。窗口事件接收线程的Looper从管道中将事件取出,交由事件处理函数进行事件响应。整个过程共有三个线程首尾相接,像三台水泵似的一层层地将事件交付给事件处理函数
InputManagerService.start()函数的作用,就像为Reader线程、Dispatcher线程这两台水泵按下开关,而Looper这台水泵在窗口创建时便已经处于运行状态了。自此,输入系统动力十足地开始运转,设备节点中的输入事件将被源源不断地抽取给事件处理者。