天天看點

Android應用程式鍵盤(Keyboard)消息處理機制分析(17)

  Step 11. InputDispatcher.dispatchOnceInnerLocked

        這個函數定義在frameworks/base/libs/ui/InputDispatcher.cpp檔案中:

void InputDispatcher::dispatchOnceInnerLocked(nsecs_t keyRepeatTimeout,  

    nsecs_t keyRepeatDelay, nsecs_t* nextWakeupTime) {  

    ......  

    // Ready to start a new event.  

    // If we don't already have a pending event, go grab one.  

    if (! mPendingEvent) {  

        if (mInboundQueue.isEmpty()) {  

            ......  

        } else {  

            // Inbound queue has at least one entry.  

            EventEntry* entry = mInboundQueue.headSentinel.next;  

            mInboundQueue.dequeue(entry);  

            mPendingEvent = entry;  

        }  

        ......  

    }  

    switch (mPendingEvent->type) {  

    case EventEntry::TYPE_KEY: {  

        KeyEntry* typedEntry = static_cast<KeyEntry*>(mPendingEvent);  

        done = dispatchKeyLocked(currentTime, typedEntry, keyRepeatTimeout,  

            &dropReason, nextWakeupTime);  

        break;  

                               }  

}  

        我們忽略了這個函數的次要邏輯,主要關注鍵盤事件的主要處理流程。首先,如果前面發生的鍵盤事件都已經處理完畢,那麼這裡的mPendingEvent就為NULL,又因為前面我們把剛剛發生的鍵盤事件加入了mInboundQueue隊列,是以,這裡mInboundQueue不為NULL,于是,這裡就把mInboundQueue隊列中的鍵盤事件取出來,放在mPendingEvent變量中:

mInboundQueue.dequeue(entry);  

mPendingEvent = entry;  

        由于這裡發生的是鍵盤事件,即mPendingEvent->type的值為EventEntry::TYPE_KEY,于是,在接下來的switch語句中就會執行dispatchKeyLocked函數來分發鍵盤消息。

        Step 12. InputDispatcher.dispatchKeyLocked

bool InputDispatcher::dispatchKeyLocked(  

        nsecs_t currentTime, KeyEntry* entry, nsecs_t keyRepeatTimeout,  

        DropReason* dropReason, nsecs_t* nextWakeupTime) {  

    // Identify targets.  

    if (! mCurrentInputTargetsValid) {  

        int32_t injectionResult = findFocusedWindowTargetsLocked(currentTime,  

            entry, nextWakeupTime);  

    // Dispatch the key.  

    dispatchEventToCurrentInputTargetsLocked(currentTime, entry, false);  

    return true;  

         InputDispatcher類中的mCurrentInputTargetsValid成員變量表示InputDispatcher是否已經标志出誰是目前激活的Activity視窗,如果沒有,就需要通過findFocusedWindowTargetsLocked函數來把它找出來。當把目前激活的Activity視窗找出來以後,接下來就調用dispatchEventToCurrentInputTargetsLocked函數把鍵盤事件分發給它了。

        我們先來看一InputDispatcher是如何找到目前激活的Activity視窗的,然後再分析它把鍵盤事件分發給目前激活Activity視窗的過程。

        Step 13. InputDispatcher.findFocusedWindowTargetsLocked

int32_t InputDispatcher::findFocusedWindowTargetsLocked(nsecs_t currentTime,  

        const EventEntry* entry, nsecs_t* nextWakeupTime) {  

    mCurrentInputTargets.clear();  

    int32_t injectionResult;  

    // If there is no currently focused window and no focused application  

    // then drop the event.  

    if (! mFocusedWindow) {  

        if (mFocusedApplication) {  

            injectionResult = handleTargetsNotReadyLocked(currentTime, entry,  

                mFocusedApplication, NULL, nextWakeupTime);  

            goto Unresponsive;  

        injectionResult = INPUT_EVENT_INJECTION_FAILED;  

        goto Failed;  

    // Check permissions.  

    if (! checkInjectionPermission(mFocusedWindow, entry->injectionState)) {  

        injectionResult = INPUT_EVENT_INJECTION_PERMISSION_DENIED;  

    // If the currently focused window is paused then keep waiting.  

    if (mFocusedWindow->paused) {  

        injectionResult = handleTargetsNotReadyLocked(currentTime, entry,  

            mFocusedApplication, mFocusedWindow, nextWakeupTime);  

        goto Unresponsive;  

    // If the currently focused window is still working on previous events then keep waiting.  

    if (! isWindowFinishedWithPreviousInputLocked(mFocusedWindow)) {  

    // Success!  Output targets.  

    injectionResult = INPUT_EVENT_INJECTION_SUCCEEDED;  

    addWindowTargetLocked(mFocusedWindow, InputTarget::FLAG_FOREGROUND, BitSet32(0));  

    return injectionResult;  

        回憶前面我們分析應用程式注冊鍵盤消息接收通道的過程時,在Step 9中,目前處于激活狀态的應用程式會通過調用InputDispatcher類setInputWindows函數把把目前獲得焦點的Activity視窗設定到mFocusedWindow中去,是以,這裡的mFocusedWindow不為NULL,于是,就通過了第一個if語句的檢查。

        第二個if語句檢查權限問題,原來,這個鍵盤事件除了是由硬體觸發的外,也可以由其它程序注入進來的,如果這個鍵盤事件是由其它程序注入進來的,那麼entry->injectState就不為NULL,它裡面包含了事件注冊者的程序ID和使用者ID,于是,這裡就會調用checkInjectionPermission來檢查這個事件注入者的程序ID和使用者ID,看看它是否具有這個權限。這裡我們不考慮這種情況,是以,這裡的entry->injectState為NULL,于是,這個if語句的檢查也通過了。

        第三個if語句檢查目前激活的Activity視窗是否是處于paused狀态,如果是的話,也不用進一步處理了。一般情況下,目前激活的Activity視窗都是處于resumed狀态的,于是,這個if語句的檢查也通過了。

        第四個if語句檢查目前激活的Activity視窗是否還正在處理前一個鍵盤事件,如果是的話,那就要等待它處理完前一個鍵盤事件後再來處理新的鍵盤事件了。這裡我們也假設目前激活的Activity視窗不是正在處理前面的鍵盤事件,是以,這個if語句的檢查也通過了。

        最後,就調用addWindowTargetLocked函數把目前激活的Activity視窗添加到InputDispatcher類的mCurrentInputTargets成員變量中去。

        Step 14. InputDispatcher.addWindowTargetLocked

void InputDispatcher::addWindowTargetLocked(const InputWindow* window, int32_t targetFlags,  

        BitSet32 pointerIds) {  

    mCurrentInputTargets.push();  

    InputTarget& target = mCurrentInputTargets.editTop();  

    target.inputChannel = window->inputChannel;  

    target.flags = targetFlags;  

    target.xOffset = - window->frameLeft;  

    target.yOffset = - window->frameTop;  

    target.pointerIds = pointerIds;  

        這個函數簡單,就是把傳進來的參數window添加到mCurrentInputTargets中去就完事了,後面InputDispatcher就會從mCurrentInputTargets中取出恰當的Activity視窗,然後把鍵盤事件分發給它。

        回到Step 12中的dispatchKeyLocked函數,它接下來就調用dispatchEventToCurrentInputTargetsLocked來進一步處理了。

        Step 15. InputDispatcher.dispatchEventToCurrentInputTargetsLocked

void InputDispatcher::dispatchEventToCurrentInputTargetsLocked(nsecs_t currentTime,  

        EventEntry* eventEntry, bool resumeWithAppendedMotionSample) {  

   ......  

   for (size_t i = 0; i < mCurrentInputTargets.size(); i++) {  

       const InputTarget& inputTarget = mCurrentInputTargets.itemAt(i);  

       ssize_t connectionIndex = getConnectionIndexLocked(inputTarget.inputChannel);  

       if (connectionIndex >= 0) {  

           sp<Connection> connection = mConnectionsByReceiveFd.valueAt(connectionIndex);  

           prepareDispatchCycleLocked(currentTime, connection, eventEntry, & inputTarget,  

               resumeWithAppendedMotionSample);  

       } else {  

           ......  

   }  

        這個函數的實作也比較簡單,前面我們已經把目前需要接受鍵盤事件的Activity視窗添加到mCurrentInputTargets中去了,是以,這裡就分别把它們取出來,然後調用prepareDispatchCycleLocked函數把鍵盤事件分發給它們處理。

        前面我們在分析應用程式注冊鍵盤消息接收通道的過程時,在Step 18中(InputDispatcher.registerInputChannel),把Server端的InputChannel封裝成了一個Connection,然後以這個InputChannel中的Receive Pipe Fd作為鍵值把這個Connection對象儲存在mConnectionsByReceiveFd中。這裡,既然我們已經通過mCurrentInputTargets得到了表示目前需要接收鍵盤事件的Activity視窗的InputTarget對象,而且這個InputTarget對象的inputChannel就表示當初在InputDispatcher中注冊的Server端InputChannel,是以,這裡就可以把這個Connection對象取出來,最後調用prepareDispatchCycleLocked函數來進一步處理。

本文轉自 Luoshengyang 51CTO部落格,原文連結:http://blog.51cto.com/shyluo/966635,如需轉載請自行聯系原作者

繼續閱讀