Android输入系统(一)输入事件传递流程和InputManagerService的诞生

关联系列
解析WMS系列
View体系系列

前言

很多同学可能会认为输入系统是不是和View的事件分发有些关联,确实是有些关联,只不过View事件分发只能算是输入系统事件传递的一部分。这个系列讲的输入系统主要是我们不常接触的,但还是需要去了解的那部分。

1. 输入事件传递流程的组成部分

输入系统是外界与Android设备交互的基础,仅凭输入系统是无法完成输入事件传递的,因此需要输入系统和Android系统的其他成员来共同完成事件传递。输入系统事件传递需要经过以下几个部分。


输入事件传递流程可以大致的分为三个部分,分别是输入系统部分、WMS处理部分和View处理部分。下面分别对这几个部分进行简单的介绍。

输入系统部分

输入系统部分主要又分为输入子系统和InputManagerService组成(以下简称IMS),在Android中还有一个IMS(IP Multimedia Subsystem)意为为IP多媒体子系统,不要搞混了。
Android的输入设备有很多种,比如屏幕、键盘、鼠标、游戏手柄、操纵杆等等,其中应用开发接触最多的屏幕。当输入设备可用时,Linux内核会在/dev/input中创建对应的设备节点。
用户操作这些输入设备时会产生各种事件比如按键事件、触摸事件、鼠标事件等。
输入事件所产生的原始信息会被Linux内核中的输入子系统采集,原始信息由Kernel space的驱动层一直传递到User space的设备节点。

Android提供了getevent和sendevent两个工具帮助开发者从设备节点读取输入事件和写入输入事件。


IMS所做的工作就是监听/dev/input下的所有的设备节点,当设备节点有数据时会将数据进行加工处理并找到合适的Window,将输入事件派发给它。

WMS处理部分

Android解析WindowManagerService(一)WMS的诞生这篇文章中我讲过WMS的职责有四种,如下图所示。

WMS的职责之一就是输入系统的中转站,WMS作为Window的管理者,会配合IMS将输入事件交由合适的Window来处理。

View处理部分

View处理部分应该是大家最熟悉的了,一般情况下,输入事件最终会交由View来处理,应用开发者就可以通过一些回调方法轻松得到这个事件的封装类并对其进行处理,比如onTouchEvent(MotionEvent ev)方法。关于View体系可以查看View体系这一系列文章。

2. IMS的诞生

输入事件传递流程的组成部分我们已经了解了,本系列主要讲解输入系统部分中IMS对输入事件的处理,在这之前我们需要了解IMS的诞生。

2.1 SyetemServer处理部分

与AMS、WMS、PMS一样,IMS的在SyetemServer进程中被创建的,SyetemServer进程用来创建系统服务,不了解它的可以查看 Android系统启动流程(三)解析SyetemServer进程启动过程 这篇文章。
从SyetemServer的入口方法main方法开始讲起,如下所示。
frameworks/base/services/java/com/android/server/SystemServer.java

public static void main(String[] args) {
       new SystemServer().run();
   }

main方法中只调用了SystemServer的run方法,如下所示。
frameworks/base/services/java/com/android/server/SystemServer.java

private void run() {
  ...
    try {
        traceBeginAndSlog("StartServices");
        //启动引导服务
        startBootstrapServices();//1
        //启动核心服务
        startCoreServices();//2
        //启动其他服务
        startOtherServices();//3
        SystemServerInitThreadPool.shutdown();
    } catch (Throwable ex) {
        Slog.e("System", "******************************************");
        Slog.e("System", "************ Failure starting system services", ex);
        throw ex;
    } finally {
        traceEnd();
    }
    ...
}

在注释1中的startBootstrapServices方法中用SystemServiceManager启动了ActivityManagerService、PowerManagerService、PackageManagerService等服务。在注释2处的startCoreServices方法中则启动了DropBoxManagerService、BatteryService、UsageStatsService和WebViewUpdateService。注释3处的startOtherServices方法中启动了CameraService、AlarmManagerService、VrManagerService等服务。这些服务的父类均为SystemService。从注释1、2、3的方法可以看出,官方把系统服务分为了三种类型,分别是引导服务、核心服务和其他服务,其中其他服务是一些非紧要和一些不需要立即启动的服务。这些系统服务总共有100多个,我们熟知的AMS和PMS属于引导服务,WMS属于其他服务。
本文要讲的IMS属于其他服务,这里列出其他服务以及它们的作用,见下表。

其他服务 作用
CameraService 摄像头相关服务
AlarmManagerService 全局定时器管理服务
InputManagerService 管理输入事件
WindowManagerService 窗口管理服务
VrManagerService VR模式管理服务
BluetoothService 蓝牙管理服务
NotificationManagerService 通知管理服务
DeviceStorageMonitorService 存储相关管理服务
LocationManagerService 定位管理服务
AudioService 音频相关管理服务
... ....

查看启动其他服务的注释3处的startOtherServices方法。
frameworks/base/services/java/com/android/server/SystemServer.java

  private void startOtherServices() {
  ...
            inputManager = new InputManagerService(context);//1
            traceEnd();
            traceBeginAndSlog("StartWindowManagerService");
            // WMS needs sensor service ready
            ConcurrentUtils.waitForFutureNoInterrupt(mSensorServiceStart, START_SENSOR_SERVICE);
            mSensorServiceStart = null;
            wm = WindowManagerService.main(context, inputManager,
                    mFactoryTestMode != FactoryTest.FACTORY_TEST_LOW_LEVEL,
                    !mFirstBoot, mOnlyCore, new PhoneWindowManager());//2
            ServiceManager.addService(Context.WINDOW_SERVICE, wm);
            ServiceManager.addService(Context.INPUT_SERVICE, inputManager);
            traceEnd();
 ...           

 }

注释1处创建了IMS,注释2处执行了WMS的main方法,其内部会创建WMS。需要注意的是,main方法的其中一个参数就是注释1处创建的IMS,在本地第1节中我们知道WMS是输入系统的中转站,其内部包含了IMS引用并不意外。紧接着将WMS和IMS添加到ServiceManager中进行统一的管理。

2.2 InputManagerService构造方法

我们接着来查看IMS的构造方法。
frameworks/base/services/core/java/com/android/server/input/InputManagerService.java

public InputManagerService(Context context) {
        this.mContext = context;
        this.mHandler = new InputManagerHandler(DisplayThread.get().getLooper());//1
        mUseDevInputEventForAudioJack =
                context.getResources().getBoolean(R.bool.config_useDevInputEventForAudioJack);
        Slog.i(TAG, "Initializing input manager, mUseDevInputEventForAudioJack="
                + mUseDevInputEventForAudioJack);
        mPtr = nativeInit(this, mContext, mHandler.getLooper().getQueue());//2

        ...
    }

注释1处用android.display线程的Looper创建了InputManagerHandler,这样InputManagerHandler会运行在android.display线程,android.display线程是系统共享的单例前台线程,这个线程内部执行了WMS的创建,具体见 Android解析WindowManagerService(一)WMS的诞生这篇文章。
注释2处调用了nativeInit方法,很明显是要通过JNI调用Navive方法。
frameworks/base/services/core/jni/com_android_server_input_InputManagerService.cpp

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* im = new NativeInputManager(contextObj, serviceObj,
            messageQueue->getLooper());//1
    im->incStrong(0);
    return reinterpret_cast<jlong>(im);
}

注释1处创建了NativeInputManager,最后会调用reinterpret_cast运算符将NativeInputManager指针强制转换并返回(重新解释比特位)。NativeInputManager的构造函数如下所示。
frameworks/base/services/core/jni/com_android_server_input_InputManagerService.cpp

NativeInputManager::NativeInputManager(jobject contextObj,
        jobject serviceObj, const sp<Looper>& looper) :
        mLooper(looper), mInteractive(true) {
    ...
    sp<EventHub> eventHub = new EventHub();
    mInputManager = new InputManager(eventHub, this, this);
}

NativeInputManager构造函数中创建了EventHub和InputManager,EventHub通过Linux内核的INotify与Epoll机制监听设备节点,通过EventHub的getEvent函数读取设备节点的增删事件和原始输入事件,本系列后续文章会详细介绍EventHub。InputManager的构造函数如下所示。
frameworks/native/services/inputflinger/InputManager.cpp

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

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

InputManager构造函数中创建了InputReader和InputDispatcher,InputReader会不断循环读取EventHub中的原始输入事件,将这些原始输入事件进行加工后交由InputDispatcher,InputDispatcher中保存了WMS中的所有Window信息(WMS会将窗口的信息实时的更新到InputDispatcher中),这样InputDispatcher就可以将输入事件派发给合适的Window。InputReader和InputDispatcher都是耗时操作,因此在initialize函数中创建了供它们运行的线程InputReaderThread和InputDispatcherThread。
InputManagerService构造方法描绘了如下的IMS简图。

从上面的简图可以看出来,IMS主要的工作都在Native层中,这些内容会在本系列的后续文章进行介绍。

感谢
《深入理解Android》卷3
《深入理解Android内核设计思想》
https://blog.csdn.net/u013604527/article/details/53432623
https://www.cnblogs.com/deng-tao/p/6094049.html

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,560评论 4 361
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,104评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,297评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,869评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,275评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,563评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,833评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,543评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,245评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,512评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,011评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,359评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,006评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,062评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,825评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,590评论 2 273
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,501评论 2 268

推荐阅读更多精彩内容