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;

                            }

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

  • 相关阅读:
    图像的卷积
    信息理论与编码中有关信源编码的笔记
    Java 数组排序
    完全平方数
    Java 作业题4
    Java 作业题3
    Java 作业题 2
    算法面试题二:旋转数组,存在重复元素,只出现一次的数字
    算法面试题一:排序算法及贪心算法
    微信小程序 发送模板消息的功能实现
  • 原文地址:https://www.cnblogs.com/simonshi/p/1691984.html
Copyright © 2011-2022 走看看