Android - Input子系统

初始Android输入系统

参考深入理解Android卷3

本文将详细讨论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从管道中将事件取出,交由事件处理函数进行事件响应。整个过程共有三个线程首尾相接,像三台水泵似的一层层地将事件交付给事件处理函数
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IxGxGJ6o-1585907160598)(https://wizardforcel.gitbooks.io/deepin-android-vol3/content/img/5-3.png)]

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

IMS的成员关系

IMS的成员关系
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LYe9eiwg-1585907160599)(https://wizardforcel.gitbooks.io/deepin-android-vol3/content/img/5-4.png)]

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值