深入理解Android输入系统--初识Android输入系统

来源:互联网 发布:分布式数据存储 编辑:程序博客网 时间:2024/05/16 01:24

初始Android输入系统

参考深入理解Android卷3

本文将详细讨论Android输入系统的工作原理,包括输入设备的管理,输入事件的加工方式以及派发流程。
重点讨论输入设备和输入事件

Android输入系统的工作原理:

监控/dev/input/下所有的设备节点,当某个节点有数据可读时,将数据读出来进行一系列的翻译加工,然后在所有的窗口中寻找合适的事件接收者,并派发给他。

输入系统简介

image
输入系统的总体流程和参与者图
图中描述了整个输入流程的参与者。
- 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的创建已经完成。在这个过程中,输入系统的重要参与者都完成了创建,并得到了如图的一套体系。

image
IMS的结构体系


IMS的启动和运行

完成了IMS的创建后,执行了IMS.start()启动IMS。
InputManager的创建过程分别为InputReader与InputDispatcher创建了承载他们的线程,但并未启动。此时的start()函数的功能就是启动这两个线程,使得InputReader和InputDispatcher开始工作。

当两个线程启动后,InputReader在其线程循环中不断地从EventHub中抽取原始输入事件,进行加工处理后将加工所得的事件放入InputDispatcher的派发发队列中。InputDispatcher则在其线程循环中将派发队列中的事件取出,查找合适的窗口,将事件写入到窗口的事件接收管道中。窗口事件接收线程的Looper从管道中将事件取出,交由事件处理函数进行事件响应。整个过程共有三个线程首尾相接,像三台水泵似的一层层地将事件交付给事件处理函数
image

InputManagerService.start()函数的作用,就像为Reader线程、Dispatcher线程这两台水泵按下开关,而Looper这台水泵在窗口创建时便已经处于运行状态了。自此,输入系统动力十足地开始运转,设备节点中的输入事件将被源源不断地抽取给事件处理者。

IMS的成员关系

IMS的成员关系
image

原创粉丝点击