android 事件傳遞機制

來源:互聯網
上載者:User

在系統啟動過程中,會載入驅動程式,初始化硬體裝置,會進入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;

                        }

 

當然這其中涉及很多細節,有興趣可以看看。同時讀寫隊列的互斥機制也值得學習。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.