Android IMS原理解析

       针对平时工作中出现的问题,学习了一下Input处理机制,对在触摸屏幕或按键后事件传到应用进行处理整个过程有了一个大概的了解,将这段时间的所学所得在此记录一下。
       IMS原理涉及的知识点比较多,为了方便阅读,总共分为六篇文章:
       Android IMS原理解析之InputReader
       Android IMS原理解析之InputDispatcher
       Android IMS原理解析之InputChannel
       Android IMS原理解析之processEvent
       Android IMS原理解析之dispatchEvent

Android输入系统

简介

       输入事件的源头是位于dev/input/下的设备节点,即:当我们触摸屏幕或按键后会在该节点下生成数据,而输入系统的终点是由WMS管理的某个窗口。
       最初的输入事件为内核生成的原始事件,而最终交付给窗口的则是KeyEvent或MotionEvent对象。
       Android输入系统的主要工作是读取设备节点中的原始事件,将其加工封装,然后派发给一个指定的窗口以及窗口中的控件。这个过程由InputManagerService系统服务为核心的多个参与者共同完成。


输入系统参与者.png

核心成员

1.Linux内核

       接受输入设备的中断,并将原始事件的输入写入设备节点中;

2.设备节点

       作为内核和IMS的桥梁,将原始事件的数据暴露给用户空间,以便IMS可以从中读取事件;

3.InputManagerService

       Android系统服务,它分为java层和native层两部分;java层负责与WMS通信,native层则是InputReader和InputDispatcher两个输入系统关键组件的运行容器;

4.EventHub

       直接访问所有的设备节点。它通过一个名为getEvent()的函数将所有输入系统相关的待处理的底层事件返回给使用者。这些事件包括原始输入事件、设备节点的增删等;

5.InputReader

       IMS中的关键组件之一,它运行于一个独立的线程中,负责管理输入设备的列表与配置,以及进行输入事件的加工处理。它通过其线程循环不断地通过getEvents()函数从EventHub中将事件取出并进行处理。对于设备节点的增删事件,它会更新输入设备列表与配置。对于原始输入事件,InputReader对其进行翻译、组装、封装为包含更多信息、更具可读性的输入事件,然后交给InputDispatcher进行派发;

6.InputReaderPolicy

       它为InputReader的事件加工处理提供一些策略配置,例如键盘布局信息等;

7.InputDispatcher

       IMS中的另一个关键组件,它也运行于一个独立的线程中。InputDispatcher中保管了来自WMS的所有窗口的信息,其收到来自InputReader的输入事件后,会在其保管的窗口中寻找合适的窗口,并将事件派发给此窗口;

8.InputDispatcherPolicy

       它为InputDispatcher的派发过程提供策略控制。例如截取某些特定的输入事件用作特殊用途,或者阻止将某些事件派发给目标窗口。一个典型的例子就是HOME键被InputDispatcherPolicy截取到PhoneWindowManager中进行处理,并阻止窗口收到HOME键按下的事件;

9.WMS

       不是输入系统的一员,但它对InputDispatcher的正常工作起到重要作用。当新建窗口时,WMS为新窗口和IMS创建了事件传递所用的通道。另外,WMS还将所有窗口的信息,包括窗口的可点击区域,焦点窗口等信息,实时的更新到IMS的InputDispatcher中,使得InputDispatcher可以正确地将事件派发到指定的窗口;

10.ViewRootImpl

       对某些窗口,如壁纸窗口、SurfaceView的窗口来说,窗口就是输入事件派发的终点。而对其他的activity、对话框等使用了Android控件系统的窗口来说,输入事件的终点是控件View。ViewRootImpl将窗口所接收的输入事件沿着控件树将事件派发给感兴趣的控件;

总结

       内核将原始事件写入设备节点中,InputReader不断地通过EventHub将原始事件取出来并翻译加工成Android输入事件,然后交给InputDispatcher。InputDispatcher根据WMS提供的窗口信息将事件交给合适的窗口。窗口的ViewRootImpl对象再沿着控件树将事件派发给感兴趣的控件。控件对其收到的事件做出响应,更新自己的画面、执行特定的动作。所有这些参与者以IMS为核心,构建Android输入体系。

IMS启动及构成

       IMS分为java和native两部分,其启动过程是从java部分的初始化开始,进而完成native部分的初始化;
       同其他核心服务一样,IMS运行在system_server进程里面,在startOtherServices()里面启动:

private void startOtherServices() {
     .......
     .......
     traceBeginAndSlog("StartInputManagerService");
     inputManager = new InputManagerService(context);
     traceEnd();

     traceBeginAndSlog("StartInputManager");
     inputManager.setWindowManagerCallbacks(wm.getInputMonitor());
     inputManager.start();
     traceEnd();
     .......
     ......
}

       通过启动逻辑可以看到,在创建完IMS实例后,先执行了setWindowManagerCallbacks然后执行了start(),接下来进入InputManagerService源码中一起看一下内部实现逻辑:

private static native long nativeInit(InputManagerService service,
            Context context, MessageQueue messageQueue);
private static native void nativeStart(long ptr);

public InputManagerService(Context context) {
    this.mContext = context;
    this.mHandler = new InputManagerHandler(DisplayThread.get().getLooper());

    mPtr = nativeInit(this, mContext, mHandler.getLooper().getQueue());

    ......
}

public void start() {
    Slog.i(TAG, "Starting input manager");
    nativeStart(mPtr);

    .......
}

//该方法设置了callbacks,后续native层的callback最终会调用到这里
public void setWindowManagerCallbacks(WindowManagerCallbacks callbacks) {
    mWindowManagerCallbacks = callbacks;
}

       在构造方法内,执行了nativeInit(),在start()中执行了nativeStart(),以上两个方法都是native方法,具体的逻辑是在native层实现的,对应的类路径为:base/services/core/jni/com_android_server_input_InputManagerService.cpp,接下来先一起看一下nativeInit():

nativeInit()
static jlong nativeInit(JNIEnv* env, jclass /* clazz */,
        jobject serviceObj, jobject contextObj, jobject messageQueueObj) {
    sp messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj);
    if (messageQueue == NULL) {
        jniThrowRuntimeException(env, "MessageQueue is not initialized.");
        return 0;
    }

    NativeInputManager* im = new NativeInputManager(contextObj, serviceObj,
            messageQueue->getLooper());
    im->incStrong(0);
    return reinterpret_cast(im);
}

       在nativeInit方法内部,创建了NativeInputManager对象:

class NativeInputManager : public virtual RefBase,
    public virtual InputReaderPolicyInterface,
    public virtual InputDispatcherPolicyInterface,
    public virtual PointerControllerPolicyInterface {
    ......
    ......
}

NativeInputManager::NativeInputManager(jobject contextObj,jobject serviceObj, const sp& looper) :
        mLooper(looper), mInteractive(true) {
    JNIEnv* env = jniEnv();

    mContextObj = env->NewGlobalRef(contextObj);
    mServiceObj = env->NewGlobalRef(serviceObj);
    .......
    .......

    sp eventHub = new EventHub();
    mInputManager = new InputManager(eventHub, this, this);
}

       可以看到,NativeInputManager继承了InputReaderPolicyInterface、InputDispatcherPolicyInterface等,后续会讲到,然后回到构造方法,在构造方法内部,先创建了EventHub,然后创建InputManager,并将自身和EventHub实例作为参数传入,再看一下InputManager,位于frameworks/native/services/inputflinger/InputManager.cpp:

InputManager::InputManager(const sp& eventHub,
        const sp& readerPolicy,
        const sp& dispatcherPolicy) {
    mDispatcher = new InputDispatcher(dispatcherPolicy);
    mReader = new InputReader(eventHub, readerPolicy, mDispatcher);
    initialize();
}

void InputManager::initialize() {
    mReaderThread = new InputReaderThread(mReader);
    mDispatcherThread = new InputDispatcherThread(mDispatcher);
}

       在实例化InputManager时,会创建InputDispatcher,然后创建InputReader,并把eventHub和mDispatcher作为参数传入,参数dispatcherPolicy和readerPolicy的实现都是NativeInputManager及参数mDispatcher,在后续事件传递过程中会用到,接下来会讲;然后执行了initialize(),分别创建了InputReader运行的线程InputReaderThread和InputDispatcher运行的线程InputDispatcherThread;
       总结一下:在执行nativeInit()后,会创建NativeInputManager对象,然后在NativeInputManager内部创建了EventHub和InputManager对象,接着在InputManager内部创建了InputReader和InputDispatcher对象,并创建了InputReaderThread和InputDispatcherThread,用一张图概况一下:

IMS主要构成.png

       Java层IMS的主要工作是为InputReaderPolicy和InputDispatcherPolicy提供实现,以及与Android系统服务进行协作,最主要的就是WMS;
       NativeInputManager位于IMS的jni层,负责native层的组件与java层的IMS的相互通信,同时它为主要工作是为InputReader和InputDispatcher提供策略请求接口InputReaderPolicyInterface和InputDispatcherPolicyInterface,策略请求被它转发为Java层的IMS,由IMS最终确定;
       InputManager由NativeInputManager创建,是InputReader和InputDispatcher的运行容器,并创建了InputReaderThread和InputDispatcherThread分别承载InputReader和InputDispatcher的运行;

nativeStart()

       接着上面分析,在执行nativeInit()后,会执行nativeStart():

static void nativeStart(JNIEnv* env, jclass /* clazz */, jlong ptr) {
    NativeInputManager* im = reinterpret_cast(ptr);

    status_t result = im->getInputManager()->start();
    if (result) {
        jniThrowRuntimeException(env, "Input manager could not be started.");
    }
}

       可以看到,实际上是调用InputManager的start()方法:

status_t InputManager::start() {
    status_t result = mDispatcherThread->run("InputDispatcher", PRIORITY_URGENT_DISPLAY);
    if (result) {
        ALOGE("Could not start InputDispatcher thread due to error %d.", result);
        return result;
    }

    result = mReaderThread->run("InputReader", PRIORITY_URGENT_DISPLAY);
    if (result) {
        ALOGE("Could not start InputReader thread due to error %d.", result);

        mDispatcherThread->requestExit();
        return result;
    }

    return OK;
}

       在start()内部,启动了InputReader运行的线程InputReaderThread和InputDispatcher运行的线程InputDispatcherThread;
       system_server在启动IMS时,执行的流程如图所示:


image.png
Thread运行

       当两个线程启动后,InputReader在其线程循环中不断地从EventHub中抽取原始输入事件,进行加工处理后将加工所得的事件放入InputDispatcher的派发队列中;InputDispatcher则在其线程循环中将派发队列中的事件取出,查找合适的窗口,将事件写入窗口的事件接收管道中;窗口事件接收线程的Looper从管道中将事件取出,交由事件处理函数进行事件响应。


处理流程.png
成员关系

       从上面的分析可以看到,在创建IMS时,涉及到许多类,先将关系整理如下,接下来通过功能详细分析:

IMS成员关系.png

       接下来从事件读取开始分析,对应文章为:Android IMS原理解析之InputReader

你可能感兴趣的:(Android IMS原理解析)