zoukankan      html  css  js  c++  java
  • android 事件传递机制

    在系统启动过程中,会加载驱动程序,初始化硬件设备,会进入bool EventHub::openPlatformInput(void)这个函数,该函数主要功能是扫描/dev/input该目录,获取输入设备。如何获取呢?通过linux API res = scan_dir(device_path); 该函数叫

    while((de = readdir(dir))) {

            strcpy(filename, de->d_name);

            open_device(devname);

    }

    不断读取目录文件,然后通过open_device()打开设备。具体打开设备函数是fd = open(deviceName, O_RDWR);以读写方式打开,该函数会调用驱动里file_operations里的实现函数。

    此时所有输入设备已经打开。

    在WindowManagerService服务类运行的时候,在构造函数中会创建内部类KeyQ对象. 该类继承之KeyInputQueue类。当然要进入该类的构造函数。在KeyInputQueue类的构造函数中会启动Thread mThread=new Thread("InputDeviceReader")这个匿名内部类线程,有该线程读驱动事件并把它放到消息队列中。具体实现是:

    Thread mThread = new Thread("InputDeviceReader") {

            public void run() {

                    RawInputEvent ev = new RawInputEvent();

                    while (true) {

                        InputDevice di;

                        readEvent(ev);

                  }

           }

    该线程持续运行,循环通过readEvent(); 该函数是个native方法,具体实现为

    static jboolean

    android_server_KeyInputQueue_readEvent(JNIEnv* env, jobject clazz,

                                              jobject event)

    {

        gLock.lock();

        sp<EventHub> hub = gHub;

        if (hub == NULL) {

            hub = new EventHub;

            gHub = hub;

        }

        gLock.unlock();

        int32_t deviceId;

        int32_t type;

        int32_t scancode, keycode;

        uint32_t flags;

        int32_t value;

        nsecs_t when;

        bool res = hub->getEvent(&deviceId, &type, &scancode, &keycode,

                &flags, &value, &when);

    }

    以上步骤获得了事件QueuedEvent。

    获得事件后通过

    private void addLocked(InputDevice device, long when, int flags,

                int classType, Object event) {

            boolean poke = mFirst.next == mLast;

            QueuedEvent ev = obtainLocked(device, when, flags, classType, event);

            QueuedEvent p = mLast.prev;

            while (p != mFirst && ev.when < p.when) {

                p = p.prev;

            }

            ev.next = p.next;

            ev.prev = p;

            p.next = ev;

            ev.next.prev = ev;

            ev.inQueue = true;

    }

    该函数加到消息队列中,该消息队列就是个双向连表,头和尾分别是 final QueuedEvent mFirst;

    final QueuedEvent mLast;  该函数就是把新消息插到尾的前面。

    消息队列有了。还需要读队列。在WindowManagerService.java 类中同时又开了个内部线程

    mInputThread = new InputDispatcherThread();

    mInputThread.start();

    该线程和刚才的写队列线程是并行的。该线程起来后,通过

    public void run() {

                while (true) {

                    try {

                        process();

                    } catch (Exception e) {

                        Log.e(TAG, "Exception in input dispatcher", e);

                    }

                }

            }

    这个process()函数来读消息。具体实现是

      private void process() {

                android.os.Process.setThreadPriority(

                        android.os.Process.THREAD_PRIORITY_URGENT_DISPLAY);

                while (true) {

                    QueuedEvent ev = mQueue.getEvent(

                        (int)((!configChanged && curTime < nextKeyTime)

                                ? (nextKeyTime-curTime) : 0));

    }

    该getEvent()函数的具体实现是

    QueuedEvent getEvent(long timeoutMS) {

            long begin = SystemClock.uptimeMillis();

            final long end = begin+timeoutMS;

            long now = begin;

            synchronized (mFirst) {

                while (mFirst.next == mLast && end > now) {

                QueuedEvent p = mFirst.next;

                mFirst.next = p.next;

                mFirst.next.prev = mFirst;

                p.inQueue = false;

                return p;

            }

    }

    持续的读消息,如果没消息就阻塞,有消息,就读取消息,所谓读取消息就得到引用,然后把该消息从双向连表中删除。得到消息后根据消息输入设备类型把消息发送到具体AP 中。如

    switch (ev.classType) {

                                 case RawInputEvent.CLASS_KEYBOARD:                             

                                    dispatchKey((KeyEvent)ev.event, 0, 0);

                                    mQueue.recycleEvent(ev);

                                    break;

                                case RawInputEvent.CLASS_TOUCHSCREEN:                             

                                    dispatchPointer(ev, (MotionEvent)ev.event, 0, 0);

                                    break;

                                case RawInputEvent.CLASS_TRACKBALL:

                                    dispatchTrackball(ev, (MotionEvent)ev.event, 0, 0);

                                    break;

                                case RawInputEvent.CLASS_CONFIGURATION_CHANGED:

                                    configChanged = true;

                                    break;

                                default:

                                    mQueue.recycleEvent(ev);

                                break;

                            }

    当然这其中涉及很多细节,有兴趣可以看看。同时读写队列的互斥机制也值得学习。

  • 相关阅读:
    Centos 7 zabbix 实战应用
    Centos7 Zabbix添加主机、图形、触发器
    Centos7 Zabbix监控部署
    Centos7 Ntp 时间服务器
    Linux 150命令之查看文件及内容处理命令 cat tac less head tail cut
    Kickstart 安装centos7
    Centos7与Centos6的区别
    Linux 150命令之 文件和目录操作命令 chattr lsattr find
    Linux 发展史与vm安装linux centos 6.9
    Linux介绍
  • 原文地址:https://www.cnblogs.com/simonshi/p/1691984.html
Copyright © 2011-2022 走看看