• i.mx android6 输入子系统分析(未完)


    参考:http://blog.csdn.net/u010312937/article/details/53285286

       https://www.jianshu.com/p/7fca94b330ea

     注意:关于下面的一些时序图,如果看不清,可以使用下载后再看,源码我已经放在GitHub上,工具为bouml,

    GitHub地址为:https://github.com/maogefff/Other/tree/master

    也可以使用我将时序图转化为PNG图片以后的,GitHub地址为:https://github.com/maogefff/Other/tree/inputSystem

    1. 框架

    InputManagerService(IMS)

    Linux内核,接受输入设备的中断,并将原始事件的数据写入设备节点中,设备节点作为内核与IMS的桥梁,将原始事件的数据暴露给用户空间,以便IMS可以从中读取事件

    InputManagerService一个android系统服务,分为Java层和Native层两部分,java层负责与WMS通信,而Native层则是InputReader和InputDispatcher两个输入系统关键组件的运行容器

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

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

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

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

    InputDispatcherPolicy,为InputDispatcher的派发过程提供策略控制,例如HOME键被InputDispatcherPolicy截取到PhoneWindowManager中处理,并阻止窗口收到HOME键按下的事件

    WMS,并不是输入系统的一员,新建窗口时,WMS为新窗口和IMS创建了事件传递所用的通道,会将窗口的可点击区域,焦点窗口等信息实时更新到IMS的InputDispatcher中,使得InputDispatcher可以正确将事件派发到指定窗口

    ViewRootImpl,对某些窗口,如壁纸窗口,SurfaceView的窗口来说,窗口就是输入事件派发的终点,而对其他的如Activity,对话框等使用了Android控件系统的窗口来说,输入事件的终点是控件

    运行流程

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

    2. 阅读代码后的总结

    2.1 启动创建流程

    图2.1非常重要;就是Android层输入子系统的创建流程,看不清可以github上下载下来看,黄色为JAVA代码,红色为CPP代码,蓝色为CPP代码所在文件

    大致思路:

    SystemServer创建整个输入子系统InputManagerService类用来管理整个输入子系统    (JAVA层)

    InputManagerService调用构造函数然后调用nativeInit(调用到C++层)创建本地管理者NativeInputManager,然后创建各种累

    下面有三个重要的功能类(这三个功能详看第一节)

    EventHub:    实例化对象为eventHub

    InputReader:   实例化对象为mReader

    InputDispatcher:实例化对象为mDispatcher

    还有三个重要的类:

    InputManager:       实例化对象为minputManager     ;主要作用为管理下面两个线程,mReaderThread和mDispathcerThread

    InputReaderThread:     实例化对象为mReaderThread    ;为一个线程的对象,主要作用为管理mReader

    InputDispatcherThread:  实例化对象为mDispathcerThread ;为一个线程的对象,主要作用为管理mDispatcher

     图2.1 创建输入系统时序图

     图2.2 类之间的包含关系

    2.2 InputReaderThread和EventHub的流程

    (一). InputReaderThread流程:

    InputReader如下图2.3所示,主要负责三个方面的工作:

    1. 获得事件(事件是通过EventHub获取,包括:1. 设备节点的增删事件; 2. 原始输入事件

    2. 对事件进行简单的处理(1. 对于设备节点的增删事件(更新设备列表和配置); 2. 原始输入事件(进行翻译和组装)

    3. 将处理后的事件分发给dispatch

    (二). EventHub流程:

     EventHub如2.3所示,主要负责检测设备节点增删事件和原始输入事件

    通过inotify检测/dev/input文件夹是否有节点增删,通过epoll分别检测/dev/input/xxx里面的内容是否有修改

    1. 通过epoll_wait等待获取事件(无事件等待)

    2. 获取增删事件后

    3. 获取原始输入事件后

    下面是伪代码:

    getEvents(RawEvent* buffer)
    {
        //检测目录是否有增删
        fd1 = inotify.init("/dev/input");
        
        //打开具体设备节点
        fd2 = open("/dev/input/event0");
        fd3 = open("/dev/input/event1");
        
        //同时检测文件和文件夹
        int pollResult = epoll_wait(fd1, fd2, fd3);
        
        switch(){
            case 增加了一个设备节点:
            buffer.type = DEVICE_ADDED;
            fd4 = open("dev/input/xxx");
            epoll_wait(fd1, fd2, fd3, fd4);   //在epoll_wait里再增加一个fd4
            break;
            
            case 减少了一个设备节点:
            buffer.type = DEVICE_REMOVE;
            epoll_wait(fd1, fd2, fd3);         //在epoll_wait里再删除掉fd4
            break;
            
            case 文件内容修改了:
            buffer.xxx = xxx;   //构造RawEvent
            break;
        }
    }

    黄色代码类名,蓝色代表所在文件(注意:时序图在EventHub.cpp当中有点问题,loop不是包含第三个,而是包含整个)

    图2.3 readThread时序图 

    3. InputManagerService(IMS)流程分析

    3.1 启动流程:

    frameworks/base/services/java/com/android/server/SystemServer.java

        public static void main(String[] args) {
            new SystemServer().run();
        }
        ...
        private void run() {
        ...
         // Start services. try { startBootstrapServices(); startCoreServices(); startOtherServices(); //其他服务里面 } catch (Throwable ex) { Slog.e("System", "******************************************"); Slog.e("System", "************ Failure starting system services", ex); throw ex; }      ... // Loop forever. Looper.loop(); throw new RuntimeException("Main thread loop unexpectedly exited"); }
        private void startOtherServices() {
            ...
            NsdService serviceDiscovery= null;
            WindowManagerService wm = null;    //窗口管理服务
            ...
            InputManagerService inputManager = null;   //输入管理服务
            ...
    
                Slog.i(TAG, "Input Manager");
                //创建输入系统
                inputManager = new InputManagerService(context);
    
                Slog.i(TAG, "Window Manager");
                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);
    
                //设置输入系统的回调函数:设置向WMS发起回调
                inputManager.setWindowManagerCallbacks(wm.getInputMonitor());
                //启动输入系统服务
                inputManager.start();
               ...
        }    
  • 相关阅读:
    安卓客户端获取手机号码
    安卓自定义控件之设计自己的提示dialog
    一步步打造自己的分页控件4
    C#winform小游戏之贪吃蛇重温C#
    android观察者模式
    Android开发之Java设计模式
    Android 用Animationlist实现逐帧动画
    图片压缩
    Android使用缓存优化ListView
    Android命令行启动程序正确使用技巧解析
  • 原文地址:https://www.cnblogs.com/maogefff/p/8466008.html
Copyright © 2020-2023  润新知