Android 事件捕捉和處理流程分析

        在Android系統中,鍵盤按鍵事件是由WindowManagerService服務來管理的,然後再以消息的形式來分發給應用程序處理,不過和普通消息不一樣,它是由硬件中斷觸發的;在上一篇文章《Android應用程序消息處理機制(Looper、Handler)分析》中,我們分析了Android應用程序的消息處理機制,本文將結合這種消息處理機制來詳細分析Android應用程序是如何獲得鍵盤按鍵消息的。

        在系統啓動的時候,SystemServer會啓動窗口管理服務WindowManagerService,WindowManagerService在啓動的時候就會通過系統輸入管理器InputManager來總負責監控鍵盤消息。這些鍵盤消息一般都是分發給當前激活的Activity窗口來處理的,因此,當前激活的Activity窗口在創建的時候,會到WindowManagerService中去註冊一個接收鍵盤消息的通道,表明它要處理鍵盤消息,而當InputManager監控到有鍵盤消息時,就會分給給它處理。噹噹前激活的Activity窗口不再處於激活狀態時,它也會到WindowManagerService中去反註冊之前的鍵盤消息接收通道,這樣,InputManager就不會再把鍵盤消息分發給它來處理。

        由於本文的內容比較多,在接下面的章節中,我們將分爲五個部分來詳細描述Android應用程序獲得鍵盤按鍵消息的過程,每一個部分都是具體描述鍵盤消息處理過程中的一個過程。結合上面的鍵盤消息處理框架,這四個過程分別是InputManager的啓動過程、應用程序註冊鍵盤消息接收通道的過程、InputManager分發鍵盤消息給應用程序的過程以及應用程序註銷鍵盤消息接收通道的過程。爲了更好地理解Android應用程序獲得鍵盤按鍵消息的整個過程,建議讀者首先閱讀Android應用程序消息處理機制(Looper、Handler)分析一文,理解了Android應用程序的消息處理機制後,就能很好的把握本文的內容。

        1. InputManager的啓動過程分析 

        前面說過,Android系統的鍵盤事件是由InputManager來監控的,而InputManager是由窗口管理服務WindowManagerService來啓動的。

        從前面一篇文章Android系統進程Zygote啓動過程的源代碼分析中,我們知道在Android系統中,Zygote進程負責啓動系統服務進程SystemServer,而系統服務進程SystemServer負責啓動系統中的各種關鍵服務,例如我們在前面兩篇文章Android應用程序安裝過程源代碼分析Android系統默認Home應用程序(Launcher)的啓動過程源代碼分析中提到的Package管理服務PackageManagerService和Activity管理服務ActivityManagerService。這裏我們所討論的窗口管理服務WindowManagerService也是由SystemServer來啓動的,具體的啓動過程這裏就不再詳述了,具體可以參考PackageManagerService和ActivityManagerService的啓動過程。

       瞭解了WindowManagerService的啓動過程之後,我們就可以繼續分析InputManager的啓動過程了。我們先來看一下InputManager啓動過程的序列圖,然後根據這個序列圖來一步步分析它的啓動過程:


點擊查看大圖

        Step 1. WindowManagerService.main

        這個函數定義在frameworks/base/services/java/com/android/server/WindowManagerService.java文件中:

  1. public class WindowManagerService extends IWindowManager.Stub  
  2.         implements Watchdog.Monitor {  
  3.     ......  
  4.   
  5.     public static WindowManagerService main(Context context,  
  6.             PowerManagerService pm, boolean haveInputMethods) {  
  7.         WMThread thr = new WMThread(context, pm, haveInputMethods);  
  8.         thr.start();  
  9.   
  10.         synchronized (thr) {  
  11.             while (thr.mService == null) {  
  12.                 try {  
  13.                     thr.wait();  
  14.                 } catch (InterruptedException e) {  
  15.                 }  
  16.             }  
  17.             return thr.mService;  
  18.         }  
  19.     }  
  20.   
  21.     ......  
  22. }  
        它通過一個線程WMThread實例來執行全局唯一的WindowManagerService實例的啓動操作。這裏調用WMThread實例thr的start成員函數時,會進入到WMThread實例thr的run函數中去。

        Step 2. WMThread.run

        這個函數定義在frameworks/base/services/java/com/android/server/WindowManagerService.java文件中:

  1. public class WindowManagerService extends IWindowManager.Stub  
  2.         implements Watchdog.Monitor {  
  3.     ......  
  4.   
  5.     static class WMThread extends Thread {  
  6.         ......  
  7.   
  8.         public void run() {  
  9.             ......  
  10.   
  11.             WindowManagerService s = new WindowManagerService(mContext, mPM,  
  12.                 mHaveInputMethods);  
  13.             ......  
  14.         }  
  15.     }  
  16.   
  17.   
  18.     ......  
  19. }  
       這裏執行的主要操作就是創建一個WindowManagerService實例,這樣會調用到WindowManagerService構造函數中去。

       Step 3. WindowManagerService<init>

       WindowManagerService類的構造函數定義在frameworks/base/services/java/com/android/server/WindowManagerService.java文件中:

  1. public class WindowManagerService extends IWindowManager.Stub  
  2.         implements Watchdog.Monitor {  
  3.     ......  
  4.   
  5.     final InputManager mInputManager;  
  6.   
  7.     ......  
  8.   
  9.     private WindowManagerService(Context context, PowerManagerService pm,  
  10.             boolean haveInputMethods) {  
  11.         ......  
  12.   
  13.         mInputManager = new InputManager(context, this);  
  14.   
  15.         ......  
  16.   
  17.         mInputManager.start();  
  18.   
  19.         ......  
  20.     }  
  21.   
  22.   
  23.     ......  
  24. }  
         這裏我們只關心InputManager的創建過程,而忽略其它無關部分。首先是創建一個InputManager實例,然後再調用它的start成員函數來監控鍵盤事件。在創建InputManager實例的過程中,會執行一些初始化工作,因此,我們先進入到InputManager類的構造函數去看看,然後再回過頭來分析它的start成員函數。

         Step 4. InputManager<init>@java

         Java層的InputManager類的構造函數定義在frameworks/base/services/java/com/android/server/InputManager.java文件中:

  1. public class InputManager {  
  2.     ......  
  3.   
  4.     public InputManager(Context context, WindowManagerService windowManagerService) {  
  5.         this.mContext = context;  
  6.         this.mWindowManagerService = windowManagerService;  
  7.   
  8.         this.mCallbacks = new Callbacks();  
  9.   
  10.         init();  
  11.     }  
  12.   
  13.     ......  
  14. }  
        這裏只是簡單地初始化InputManager類的一些成員變量,然後調用init函數進一步執行初始化操作。

        Step 5. InputManager.init

        這個函數定義在frameworks/base/services/java/com/android/server/InputManager.java文件中:

  1. public class InputManager {  
  2.     ......  
  3.   
  4.     private void init() {  
  5.         Slog.i(TAG, "Initializing input manager");  
  6.         nativeInit(mCallbacks);  
  7.     }  
  8.   
  9.     ......  
  10. }  
       函數init通過調用本地方法nativeInit來執行C++層的相關初始化操作。

       Step 6. InputManager.nativeInit

       這個函數定義在frameworks/base/services/jni$ vi com_android_server_InputManager.cpp文件中:

  1. static void android_server_InputManager_nativeInit(JNIEnv* env, jclass clazz,  
  2.         jobject callbacks) {  
  3.     if (gNativeInputManager == NULL) {  
  4.         gNativeInputManager = new NativeInputManager(callbacks);  
  5.     } else {  
  6.         LOGE("Input manager already initialized.");  
  7.         jniThrowRuntimeException(env, "Input manager already initialized.");  
  8.     }  
  9. }  
        這個函數的作用是創建一個NativeInputManager實例,並保存在gNativeInputManager變量中。由於是第一次調用到這裏,因此,gNativeInputManager爲NULL,於是就會new一個NativeInputManager對象出來,這樣就會執行NativeInputManager類的構造函數來執其它的初始化操作。

       Step 7. NativeInputManager<init>

       NativeInputManager類的構造函數定義在frameworks/base/services/jni$ vi com_android_server_InputManager.cpp文件中:

  1. NativeInputManager::NativeInputManager(jobject callbacksObj) :  
  2.     mFilterTouchEvents(-1), mFilterJumpyTouchEvents(-1), mVirtualKeyQuietTime(-1),  
  3.     mMaxEventsPerSecond(-1),  
  4.     mDisplayWidth(-1), mDisplayHeight(-1), mDisplayOrientation(ROTATION_0) {  
  5.     JNIEnv* env = jniEnv();  
  6.   
  7.     mCallbacksObj = env->NewGlobalRef(callbacksObj);  
  8.   
  9.     sp<EventHub> eventHub = new EventHub();  
  10.     mInputManager = new InputManager(eventHub, thisthis);  
  11. }  
        這裏只要是創建了一個EventHub實例,並且把這個EventHub作爲參數來創建InputManager對象。注意,這裏的InputManager類是定義在C++層的,和前面在Java層的InputManager不一樣,不過它們是對應關係。EventHub類是真正執行監控鍵盤事件操作的地方,後面我們會進一步分析到,現在我們主要關心InputManager實例的創建過程,它會InputManager類的構造函數裏面執行一些初始化操作。

        Step 8. InputManager<init>@C++

        C++層的InputManager類的構造函數定義在frameworks/base/libs/ui/InputManager.cpp 文件中:

  1. InputManager::InputManager(  
  2.         const sp<EventHubInterface>& eventHub,  
  3.         const sp<InputReaderPolicyInterface>& readerPolicy,  
  4.         const sp<InputDispatcherPolicyInterface>& dispatcherPolicy) {  
  5.     mDispatcher = new InputDispatcher(dispatcherPolicy);  
  6.     mReader = new InputReader(eventHub, readerPolicy, mDispatcher);  
  7.     initialize();  
  8. }  
        這裏主要是創建了一個InputDispatcher對象和一個InputReader對象,並且分別保存在成員變量mDispatcher和mReader中。InputDispatcher類是負責把鍵盤消息分發給當前激活的Activity窗口的,而InputReader類則是通過EventHub類來實現讀取鍵盤事件的,後面我們會進一步分析。創建了這兩個對象後,還要調用initialize函數來執行其它的初始化操作。

        Step 9. InputManager.initialize

        這個函數定義在frameworks/base/libs/ui/InputManager.cpp 文件中:

  1. void InputManager::initialize() {  
  2.     mReaderThread = new InputReaderThread(mReader);  
  3.     mDispatcherThread = new InputDispatcherThread(mDispatcher);  
  4. }  
        這個函數創建了一個InputReaderThread線程實例和一個InputDispatcherThread線程實例,並且分別保存在成員變量mReaderThread和mDispatcherThread中。這裏的InputReader實列mReader就是通過這裏的InputReaderThread線程實列mReaderThread來讀取鍵盤事件的,而InputDispatcher實例mDispatcher則是通過這裏的InputDispatcherThread線程實例mDisptacherThread來分發鍵盤消息的。

        至此,InputManager的初始化工作就完成了,在回到Step 3中繼續分析InputManager的進一步啓動過程之前,我們先來作一個小結,看看這個初始化過程都做什麼事情:

        A. 在Java層中的WindowManagerService中創建了一個InputManager對象,由它來負責管理Android應用程序框架層的鍵盤消息處理;

        B. 在C++層也相應地創建一個InputManager本地對象來負責監控鍵盤事件;

        C. 在C++層中的InputManager對象中,分別創建了一個InputReader對象和一個InputDispatcher對象,前者負責讀取系統中的鍵盤消息,後者負責把鍵盤消息分發出去;

        D. InputReader對象和一個InputDispatcher對象分別是通過InputReaderThread線程實例和InputDispatcherThread線程實例來實鍵盤消息的讀取和分發的。

        有了這些對象之後,萬事就俱備了,回到Step 3中,調用InputManager類的start函數來執行真正的啓動操作。

        Step 10. InputManager.start

        這個函數定義在frameworks/base/services/java/com/android/server/InputManager.java文件中:

  1. public class InputManager {  
  2.     ......  
  3.   
  4.     public void start() {  
  5.         Slog.i(TAG, "Starting input manager");  
  6.         nativeStart();  
  7.     }  
  8.   
  9.     ......  
  10. }  
        這個函數通過調用本地方法nativeStart來執行進一步的啓動操作。

        Step 11. InputManager.nativeStart

        這個函數定義在frameworks/base/services/jni$ vi com_android_server_InputManager.cpp文件中:

  1. static void android_server_InputManager_nativeStart(JNIEnv* env, jclass clazz) {  
  2.     if (checkInputManagerUnitialized(env)) {  
  3.         return;  
  4.     }  
  5.   
  6.     status_t result = gNativeInputManager->getInputManager()->start();  
  7.     if (result) {  
  8.         jniThrowRuntimeException(env, "Input manager could not be started.");  
  9.     }  
  10. }  
        這裏的gNativeInputManager對象是在前面的Step 6中創建的,通過它的getInputManager函數可以返回C++層的InputManager對象,接着調用這個InputManager對象的start函數。

        Step 12. InputManager.start

        這個函數定義在frameworks/base/libs/ui/InputManager.cpp 文件中:

  1. status_t InputManager::start() {  
  2.     status_t result = mDispatcherThread->run("InputDispatcher", PRIORITY_URGENT_DISPLAY);  
  3.     if (result) {  
  4.         LOGE("Could not start InputDispatcher thread due to error %d.", result);  
  5.         return result;  
  6.     }  
  7.   
  8.     result = mReaderThread->run("InputReader", PRIORITY_URGENT_DISPLAY);  
  9.     if (result) {  
  10.         LOGE("Could not start InputReader thread due to error %d.", result);  
  11.   
  12.         mDispatcherThread->requestExit();  
  13.         return result;  
  14.     }  
  15.   
  16.     return OK;  
  17. }  
        這個函數主要就是分別啓動一個InputDispatcherThread線程和一個InputReaderThread線程來讀取和分發鍵盤消息的了。這裏的InputDispatcherThread線程對象mDispatcherThread和InputReaderThread線程對象是在前面的Step 9中創建的,調用了它們的run函數後,就會進入到它們的threadLoop函數中去,只要threadLoop函數返回true,函數threadLoop就會一直被循環調用,於是這兩個線程就起到了不斷地讀取和分發鍵盤消息的作用。

        我們先來分析InputDispatcherThread線程分發消息的過程,然後再回過頭來分析InputReaderThread線程讀取消息的過程。

        Step 13. InputDispatcherThread.threadLoop

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

  1. bool InputDispatcherThread::threadLoop() {  
  2.     mDispatcher->dispatchOnce();  
  3.     return true;  
  4. }  
        這裏的成員變量mDispatcher即爲在前面Step 8中創建的InputDispatcher對象,調用它的dispatchOnce成員函數執行一次鍵盤消息分發的操作。

        Step 14. InputDispatcher.dispatchOnce

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

  1. void InputDispatcher::dispatchOnce() {  
  2.     nsecs_t keyRepeatTimeout = mPolicy->getKeyRepeatTimeout();  
  3.     nsecs_t keyRepeatDelay = mPolicy->getKeyRepeatDelay();  
  4.   
  5.     nsecs_t nextWakeupTime = LONG_LONG_MAX;  
  6.     { // acquire lock  
  7.         AutoMutex _l(mLock);  
  8.         dispatchOnceInnerLocked(keyRepeatTimeout, keyRepeatDelay, & nextWakeupTime);  
  9.   
  10.         if (runCommandsLockedInterruptible()) {  
  11.             nextWakeupTime = LONG_LONG_MIN;  // force next poll to wake up immediately  
  12.         }  
  13.     } // release lock  
  14.   
  15.     // Wait for callback or timeout or wake.  (make sure we round up, not down)  
  16.     nsecs_t currentTime = now();  
  17.     int32_t timeoutMillis;  
  18.     if (nextWakeupTime > currentTime) {  
  19.         uint64_t timeout = uint64_t(nextWakeupTime - currentTime);  
  20.         timeout = (timeout + 999999LL) / 1000000LL;  
  21.         timeoutMillis = timeout > INT_MAX ? -1 : int32_t(timeout);  
  22.     } else {  
  23.         timeoutMillis = 0;  
  24.     }  
  25.   
  26.     mLooper->pollOnce(timeoutMillis);  
  27. }  
        這個函數很簡單,把鍵盤消息交給dispatchOnceInnerLocked函數來處理,這個過程我們在後面再詳細分析,然後調用mLooper->pollOnce函數等待下一次鍵盤事件的發生。這裏的成員變量mLooper的類型爲Looper,它定義在C++層中,具體可以參考前面Android應用程序消息處理機制(Looper、Handler)分析一文。

        Step 15. Looper.pollOnce

        這個函數定義在frameworks/base/libs/utils/Looper.cpp文件中,具體可以參考前面Android應用程序消息處理機制(Looper、Handler)分析一文,這裏就不再詳述了。總的來說,就是在Looper類中,會創建一個管道,當調用Looper類的pollOnce函數時,如果管道中沒有內容可讀,那麼當前線程就會進入到空閒等待狀態;當有鍵盤事件發生時,InputReader就會往這個管道中寫入新的內容,這樣就會喚醒前面正在等待鍵盤事件發生的線程。

        InputDispatcher類分發消息的過程就暫時分析到這裏,後面會有更進一步的分析,現在,我們回到Step 12中,接着分析InputReader類讀取鍵盤事件的過程。在調用了InputReaderThread線程類的run就函數後,同樣會進入到InputReaderThread線程類的threadLoop函數中去。

        Step 16. InputReaderThread.threadLoop

        這個函數定義在frameworks/base/libs/ui/InputReader.cpp文件中:

  1. bool InputReaderThread::threadLoop() {  
  2.     mReader->loopOnce();  
  3.     return true;  
  4. }  
       這裏的成員變量mReader即爲在前面Step 8中創建的InputReader對象,調用它的loopOnce成員函數執行一次鍵盤事件的讀取操作。

       Step 17. InputReader.loopOnce

       這個函數定義在frameworks/base/libs/ui/InputReader.cpp文件中:

  1. void InputReader::loopOnce() {  
  2.     RawEvent rawEvent;  
  3.     mEventHub->getEvent(& rawEvent);  
  4.   
  5. #if DEBUG_RAW_EVENTS  
  6.     LOGD("Input event: device=0x%x type=0x%x scancode=%d keycode=%d value=%d",  
  7.         rawEvent.deviceId, rawEvent.type, rawEvent.scanCode, rawEvent.keyCode,  
  8.         rawEvent.value);  
  9. #endif  
  10.   
  11.     process(& rawEvent);  
  12. }  
        這裏通過成員函數mEventHub來負責鍵盤消息的讀取工作,如果當前有鍵盤事件發生或者有鍵盤事件等待處理,通過mEventHub的getEvent函數就可以得到這個事件,然後交給process函數進行處理,這個函數主要就是喚醒前面的InputDispatcherThread線程,通知它有新的鍵盤事件發生了,它需要進行一次鍵盤消息的分發操作了,這個函數我們後面再進一步詳細分析;如果沒有鍵盤事件發生或者沒有鍵盤事件等待處理,那麼調用mEventHub的getEvent函數時就會進入等待狀態。

        Step 18. EventHub.getEvent

        這個函數定義在frameworks/base/libs/ui/EventHub.cpp文件中:

  1. bool EventHub::getEvent(RawEvent* outEvent)  
  2. {  
  3.     outEvent->deviceId = 0;  
  4.     outEvent->type = 0;  
  5.     outEvent->scanCode = 0;  
  6.     outEvent->keyCode = 0;  
  7.     outEvent->flags = 0;  
  8.     outEvent->value = 0;  
  9.     outEvent->when = 0;  
  10.   
  11.     // Note that we only allow one caller to getEvent(), so don't need  
  12.     // to do locking here...  only when adding/removing devices.  
  13.   
  14.     if (!mOpened) {  
  15.         mError = openPlatformInput() ? NO_ERROR : UNKNOWN_ERROR;  
  16.         mOpened = true;  
  17.         mNeedToSendFinishedDeviceScan = true;  
  18.     }  
  19.   
  20.     for (;;) {  
  21.         // Report any devices that had last been added/removed.  
  22.         if (mClosingDevices != NULL) {  
  23.             device_t* device = mClosingDevices;  
  24.             LOGV("Reporting device closed: id=0x%x, name=%s\n",  
  25.                 device->id, device->path.string());  
  26.             mClosingDevices = device->next;  
  27.             if (device->id == mFirstKeyboardId) {  
  28.                 outEvent->deviceId = 0;  
  29.             } else {  
  30.                 outEvent->deviceId = device->id;  
  31.             }  
  32.             outEvent->type = DEVICE_REMOVED;  
  33.             outEvent->when = systemTime(SYSTEM_TIME_MONOTONIC);  
  34.             delete device;  
  35.             mNeedToSendFinishedDeviceScan = true;  
  36.             return true;  
  37.         }  
  38.   
  39.         if (mOpeningDevices != NULL) {  
  40.             device_t* device = mOpeningDevices;  
  41.             LOGV("Reporting device opened: id=0x%x, name=%s\n",  
  42.                 device->id, device->path.string());  
  43.             mOpeningDevices = device->next;  
  44.             if (device->id == mFirstKeyboardId) {  
  45.                 outEvent->deviceId = 0;  
  46.             } else {  
  47.                 outEvent->deviceId = device->id;  
  48.             }  
  49.             outEvent->type = DEVICE_ADDED;  
  50.             outEvent->when = systemTime(SYSTEM_TIME_MONOTONIC);  
  51.             mNeedToSendFinishedDeviceScan = true;  
  52.             return true;  
  53.         }  
  54.   
  55.         if (mNeedToSendFinishedDeviceScan) {  
  56.             mNeedToSendFinishedDeviceScan = false;  
  57.             outEvent->type = FINISHED_DEVICE_SCAN;  
  58.             outEvent->when = systemTime(SYSTEM_TIME_MONOTONIC);  
  59.             return true;  
  60.         }  
  61.   
  62.         // Grab the next input event.  
  63.         for (;;) {  
  64.             // Consume buffered input events, if any.  
  65.             if (mInputBufferIndex < mInputBufferCount) {  
  66.                 const struct input_event& iev = mInputBufferData[mInputBufferIndex++];  
  67.                 const device_t* device = mDevices[mInputDeviceIndex];  
  68.   
  69.                 LOGV("%s got: t0=%d, t1=%d, type=%d, code=%d, v=%d", device->path.string(),  
  70.                     (int) iev.time.tv_sec, (int) iev.time.tv_usec, iev.type, iev.code, iev.value);  
  71.                 if (device->id == mFirstKeyboardId) {  
  72.                     outEvent->deviceId = 0;  
  73.                 } else {  
  74.                     outEvent->deviceId = device->id;  
  75.                 }  
  76.                 outEvent->type = iev.type;  
  77.                 outEvent->scanCode = iev.code;  
  78.                 if (iev.type == EV_KEY) {  
  79.                     status_t err = device->layoutMap->map(iev.code,  
  80.                         & outEvent->keyCode, & outEvent->flags);  
  81.                     LOGV("iev.code=%d keyCode=%d flags=0x%08x err=%d\n",  
  82.                         iev.code, outEvent->keyCode, outEvent->flags, err);  
  83.                     if (err != 0) {  
  84.                         outEvent->keyCode = AKEYCODE_UNKNOWN;  
  85.                         outEvent->flags = 0;  
  86.                     }  
  87.                 } else {  
  88.                     outEvent->keyCode = iev.code;  
  89.                 }  
  90.                 outEvent->value = iev.value;  
  91.   
  92.                 // Use an event timestamp in the same timebase as  
  93.                 // java.lang.System.nanoTime() and android.os.SystemClock.uptimeMillis()  
  94.                 // as expected by the rest of the system.  
  95.                 outEvent->when = systemTime(SYSTEM_TIME_MONOTONIC);  
  96.                 return true;  
  97.             }  
  98.   
  99.             // Finish reading all events from devices identified in previous poll().  
  100.             // This code assumes that mInputDeviceIndex is initially 0 and that the  
  101.             // revents member of pollfd is initialized to 0 when the device is first added.  
  102.             // Since mFDs[0] is used for inotify, we process regular events starting at index 1.  
  103.             mInputDeviceIndex += 1;  
  104.             if (mInputDeviceIndex >= mFDCount) {  
  105.                 break;  
  106.             }  
  107.   
  108.             const struct pollfd& pfd = mFDs[mInputDeviceIndex];  
  109.             if (pfd.revents & POLLIN) {  
  110.                 int32_t readSize = read(pfd.fd, mInputBufferData,  
  111.                     sizeof(struct input_event) * INPUT_BUFFER_SIZE);  
  112.                 if (readSize < 0) {  
  113.                     if (errno != EAGAIN && errno != EINTR) {  
  114.                         LOGW("could not get event (errno=%d)", errno);  
  115.                     }  
  116.                 } else if ((readSize % sizeof(struct input_event)) != 0) {  
  117.                     LOGE("could not get event (wrong size: %d)", readSize);  
  118.                 } else {  
  119.                     mInputBufferCount = readSize / sizeof(struct input_event);  
  120.                     mInputBufferIndex = 0;  
  121.                 }  
  122.             }  
  123.         }  
  124.   
  125.         ......  
  126.   
  127.         mInputDeviceIndex = 0;  
  128.   
  129.         // Poll for events.  Mind the wake lock dance!  
  130.         // We hold a wake lock at all times except during poll().  This works due to some  
  131.         // subtle choreography.  When a device driver has pending (unread) events, it acquires  
  132.         // a kernel wake lock.  However, once the last pending event has been read, the device  
  133.         // driver will release the kernel wake lock.  To prevent the system from going to sleep  
  134.         // when this happens, the EventHub holds onto its own user wake lock while the client  
  135.         // is processing events.  Thus the system can only sleep if there are no events  
  136.         // pending or currently being processed.  
  137.         release_wake_lock(WAKE_LOCK_ID);  
  138.   
  139.         int pollResult = poll(mFDs, mFDCount, -1);  
  140.   
  141.         acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_ID);  
  142.   
  143.         if (pollResult <= 0) {  
  144.             if (errno != EINTR) {  
  145.                 LOGW("poll failed (errno=%d)\n", errno);  
  146.                 usleep(100000);  
  147.             }  
  148.         }  
  149.   
  150.     }  
  151. }  
        這個函數比較長,我們一步一步來分析。

        首先,如果是第一次進入到這個函數中時,成員變量mOpened的值爲false,於是就會調用openPlatformInput函數來打開系統輸入設備,在本文中,我們主要討論的輸入設備就是鍵盤了。打開了這些輸入設備文件後,就可以對這些輸入設備進行是監控了。如果不是第一次進入到這個函數,那麼就會分析當前有沒有輸入事件發生,如果有,就返回這個事件,否則就會進入等待狀態,等待下一次輸入事件的發生。在我們這個場景中,就是等待下一次鍵盤事件的發生了。

       我們先分析openPlatformInput函數的實現,然後回過頭來分析這個getEvent函數的具體的實現。

       Step 19. EventHub.openPlatformInput

       這個函數定義在frameworks/base/libs/ui/EventHub.cpp文件中:

  1. bool EventHub::openPlatformInput(void)  
  2. {  
  3.     ......  
  4.   
  5.     res = scanDir(device_path);  
  6.     if(res < 0) {  
  7.         LOGE("scan dir failed for %s\n", device_path);  
  8.     }  
  9.   
  10.     return true;  
  11. }  
        這個函數主要是掃描device_path目錄下的設備文件,然後打開它們,這裏的變量device_path定義在frameworks/base/libs/ui/EventHub.cpp文件開始的地方:
  1. static const char *device_path = "/dev/input";  
        在設備目錄/dev/input中,一般有三個設備文件存在,分別是event0、mice和mouse0設備文件,其中,鍵盤事件就包含在event0設備文件中了。

        Step 20. EventHub.scanDir

        這個函數定義在frameworks/base/libs/ui/EventHub.cpp文件中:

  1. int EventHub::scanDir(const char *dirname)  
  2. {  
  3.     char devname[PATH_MAX];  
  4.     char *filename;  
  5.     DIR *dir;  
  6.     struct dirent *de;  
  7.     dir = opendir(dirname);  
  8.     if(dir == NULL)  
  9.         return -1;  
  10.     strcpy(devname, dirname);  
  11.     filename = devname + strlen(devname);  
  12.     *filename++ = '/';  
  13.     while((de = readdir(dir))) {  
  14.         if(de->d_name[0] == '.' &&  
  15.             (de->d_name[1] == '\0' ||  
  16.             (de->d_name[1] == '.' && de->d_name[2] == '\0')))  
  17.             continue;  
  18.         strcpy(filename, de->d_name);  
  19.         openDevice(devname);  
  20.     }  
  21.     closedir(dir);  
  22.     return 0;  
  23. }  
       根據上面一步的分析,這個函數主要就是調用openDevice函數來分別打開/dev/input/event0、/dev/input/mice和/dev/input/mouse0三個設備文件了。

       Step 21. EventHub.openDevice
       這個函數定義在frameworks/base/libs/ui/EventHub.cpp文件中:

  1. int EventHub::openDevice(const char *deviceName) {  
  2.     int version;  
  3.     int fd;  
  4.     struct pollfd *new_mFDs;  
  5.     device_t **new_devices;  
  6.     char **new_device_names;  
  7.     char name[80];  
  8.     char location[80];  
  9.     char idstr[80];  
  10.     struct input_id id;  
  11.   
  12.     LOGV("Opening device: %s", deviceName);  
  13.   
  14.     AutoMutex _l(mLock);  
  15.   
  16.     fd = open(deviceName, O_RDWR);  
  17.     if(fd < 0) {  
  18.         LOGE("could not open %s, %s\n", deviceName, strerror(errno));  
  19.         return -1;  
  20.     }  
  21.   
  22.     ......  
  23.   
  24.     int devid = 0;  
  25.     while (devid < mNumDevicesById) {  
  26.         if (mDevicesById[devid].device == NULL) {  
  27.             break;  
  28.         }  
  29.         devid++;  
  30.     }  
  31.       
  32.     ......  
  33.   
  34.     mDevicesById[devid].seq = (mDevicesById[devid].seq+(1<<SEQ_SHIFT))&SEQ_MASK;  
  35.     if (mDevicesById[devid].seq == 0) {  
  36.         mDevicesById[devid].seq = 1<<SEQ_SHIFT;  
  37.     }  
  38.   
  39.     new_mFDs = (pollfd*)realloc(mFDs, sizeof(mFDs[0]) * (mFDCount + 1));  
  40.     new_devices = (device_t**)realloc(mDevices, sizeof(mDevices[0]) * (mFDCount + 1));  
  41.     if (new_mFDs == NULL || new_devices == NULL) {  
  42.         LOGE("out of memory");  
  43.         return -1;  
  44.     }  
  45.     mFDs = new_mFDs;  
  46.     mDevices = new_devices;  
  47.   
  48.     ......  
  49.   
  50.     device_t* device = new device_t(devid|mDevicesById[devid].seq, deviceName, name);  
  51.     if (device == NULL) {  
  52.         LOGE("out of memory");  
  53.         return -1;  
  54.     }  
  55.   
  56.     device->fd = fd;  
  57.     mFDs[mFDCount].fd = fd;  
  58.     mFDs[mFDCount].events = POLLIN;  
  59.     mFDs[mFDCount].revents = 0;  
  60.   
  61.     // Figure out the kinds of events the device reports.  
  62.   
  63.     uint8_t key_bitmask[sizeof_bit_array(KEY_MAX + 1)];  
  64.     memset(key_bitmask, 0, sizeof(key_bitmask));  
  65.   
  66.     LOGV("Getting keys...");  
  67.     if (ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(key_bitmask)), key_bitmask) >= 0) {  
  68.         // See if this is a keyboard.  Ignore everything in the button range except for  
  69.         // gamepads which are also considered keyboards.  
  70.         if (containsNonZeroByte(key_bitmask, 0, sizeof_bit_array(BTN_MISC))  
  71.             || containsNonZeroByte(key_bitmask, sizeof_bit_array(BTN_GAMEPAD),  
  72.             sizeof_bit_array(BTN_DIGI))  
  73.             || containsNonZeroByte(key_bitmask, sizeof_bit_array(KEY_OK),  
  74.             sizeof_bit_array(KEY_MAX + 1))) {  
  75.                 device->classes |= INPUT_DEVICE_CLASS_KEYBOARD;  
  76.   
  77.                 device->keyBitmask = new uint8_t[sizeof(key_bitmask)];  
  78.                 if (device->keyBitmask != NULL) {  
  79.                     memcpy(device->keyBitmask, key_bitmask, sizeof(key_bitmask));  
  80.                 } else {  
  81.                     delete device;  
  82.                     LOGE("out of memory allocating key bitmask");  
  83.                     return -1;  
  84.                 }  
  85.         }  
  86.     }  
  87.   
  88.     ......  
  89.   
  90.     if ((device->classes & INPUT_DEVICE_CLASS_KEYBOARD) != 0) {  
  91.         char tmpfn[sizeof(name)];  
  92.         char keylayoutFilename[300];  
  93.   
  94.         // a more descriptive name  
  95.         device->name = name;  
  96.   
  97.         // replace all the spaces with underscores  
  98.         strcpy(tmpfn, name);  
  99.         for (char *p = strchr(tmpfn, ' '); p && *p; p = strchr(tmpfn, ' '))  
  100.             *p = '_';  
  101.   
  102.         // find the .kl file we need for this device  
  103.         const char* root = getenv("ANDROID_ROOT");  
  104.         snprintf(keylayoutFilename, sizeof(keylayoutFilename),  
  105.             "%s/usr/keylayout/%s.kl", root, tmpfn);  
  106.         bool defaultKeymap = false;  
  107.         if (access(keylayoutFilename, R_OK)) {  
  108.             snprintf(keylayoutFilename, sizeof(keylayoutFilename),  
  109.                 "%s/usr/keylayout/%s", root, "qwerty.kl");  
  110.             defaultKeymap = true;  
  111.         }  
  112.         status_t status = device->layoutMap->load(keylayoutFilename);  
  113.         if (status) {  
  114.             LOGE("Error %d loading key layout.", status);  
  115.         }  
  116.   
  117.         // tell the world about the devname (the descriptive name)  
  118.         if (!mHaveFirstKeyboard && !defaultKeymap && strstr(name, "-keypad")) {  
  119.             // the built-in keyboard has a well-known device ID of 0,  
  120.             // this device better not go away.  
  121.             mHaveFirstKeyboard = true;  
  122.             mFirstKeyboardId = device->id;  
  123.             property_set("hw.keyboards.0.devname", name);  
  124.         } else {  
  125.             // ensure mFirstKeyboardId is set to -something-.  
  126.             if (mFirstKeyboardId == 0) {  
  127.                 mFirstKeyboardId = device->id;  
  128.             }  
  129.         }  
  130.         char propName[100];  
  131.         sprintf(propName, "hw.keyboards.%u.devname", device->id);  
  132.         property_set(propName, name);  
  133.   
  134.         // 'Q' key support = cheap test of whether this is an alpha-capable kbd  
  135.         if (hasKeycodeLocked(device, AKEYCODE_Q)) {  
  136.             device->classes |= INPUT_DEVICE_CLASS_ALPHAKEY;  
  137.         }  
  138.   
  139.         // See if this device has a DPAD.  
  140.         if (hasKeycodeLocked(device, AKEYCODE_DPAD_UP) &&  
  141.             hasKeycodeLocked(device, AKEYCODE_DPAD_DOWN) &&  
  142.             hasKeycodeLocked(device, AKEYCODE_DPAD_LEFT) &&  
  143.             hasKeycodeLocked(device, AKEYCODE_DPAD_RIGHT) &&  
  144.             hasKeycodeLocked(device, AKEYCODE_DPAD_CENTER)) {  
  145.                 device->classes |= INPUT_DEVICE_CLASS_DPAD;  
  146.         }  
  147.   
  148.         // See if this device has a gamepad.  
  149.         for (size_t i = 0; i < sizeof(GAMEPAD_KEYCODES)/sizeof(GAMEPAD_KEYCODES[0]); i++) {  
  150.             if (hasKeycodeLocked(device, GAMEPAD_KEYCODES[i])) {  
  151.                 device->classes |= INPUT_DEVICE_CLASS_GAMEPAD;  
  152.                 break;  
  153.             }  
  154.         }  
  155.   
  156.         LOGI("New keyboard: device->id=0x%x devname='%s' propName='%s' keylayout='%s'\n",  
  157.             device->id, name, propName, keylayoutFilename);  
  158.     }  
  159.   
  160.     ......  
  161.   
  162.     mDevicesById[devid].device = device;  
  163.     device->next = mOpeningDevices;  
  164.     mOpeningDevices = device;  
  165.     mDevices[mFDCount] = device;  
  166.   
  167.     mFDCount++;  
  168.     return 0;  
  169. }  
         函數首先根據文件名來打開這個設備文件:
  1. fd = open(deviceName, O_RDWR);  
        系統中所有輸入設備文件信息都保存在成員變量mDevicesById中,因此,先在mDevicesById找到一個空位置來保存當前打開的設備文件信息:
  1. mDevicesById[devid].seq = (mDevicesById[devid].seq+(1<<SEQ_SHIFT))&SEQ_MASK;  
  2. if (mDevicesById[devid].seq == 0) {  
  3.     mDevicesById[devid].seq = 1<<SEQ_SHIFT;  
  4. }  
        找到了空閒位置後,就爲這個輸入設備文件創建相應的device_t信息:
  1. mDevicesById[devid].seq = (mDevicesById[devid].seq+(1<<SEQ_SHIFT))&SEQ_MASK;  
  2. if (mDevicesById[devid].seq == 0) {  
  3.     mDevicesById[devid].seq = 1<<SEQ_SHIFT;  
  4. }  
  5.   
  6. new_mFDs = (pollfd*)realloc(mFDs, sizeof(mFDs[0]) * (mFDCount + 1));  
  7. new_devices = (device_t**)realloc(mDevices, sizeof(mDevices[0]) * (mFDCount + 1));  
  8. if (new_mFDs == NULL || new_devices == NULL) {  
  9.     LOGE("out of memory");  
  10.     return -1;  
  11. }  
  12. mFDs = new_mFDs;  
  13. mDevices = new_devices;  
  14.   
  15. ......  
  16.   
  17. device_t* device = new device_t(devid|mDevicesById[devid].seq, deviceName, name);  
  18. if (device == NULL) {  
  19.     LOGE("out of memory");  
  20.     return -1;  
  21. }  
  22.   
  23. device->fd = fd;  

        同時,這個設備文件還會保存在數組mFDs中:

  1. mFDs[mFDCount].fd = fd;  
  2. mFDs[mFDCount].events = POLLIN;  
  3. mFDs[mFDCount].revents = 0;  
       接下來查看這個設備是不是鍵盤:
  1. // Figure out the kinds of events the device reports.  
  2.   
  3. uint8_t key_bitmask[sizeof_bit_array(KEY_MAX + 1)];  
  4. memset(key_bitmask, 0, sizeof(key_bitmask));  
  5.   
  6. LOGV("Getting keys...");  
  7. if (ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(key_bitmask)), key_bitmask) >= 0) {  
  8.     // See if this is a keyboard.  Ignore everything in the button range except for  
  9.     // gamepads which are also considered keyboards.  
  10.     if (containsNonZeroByte(key_bitmask, 0, sizeof_bit_array(BTN_MISC))  
  11.         || containsNonZeroByte(key_bitmask, sizeof_bit_array(BTN_GAMEPAD),  
  12.         sizeof_bit_array(BTN_DIGI))  
  13.         || containsNonZeroByte(key_bitmask, sizeof_bit_array(KEY_OK),  
  14.         sizeof_bit_array(KEY_MAX + 1))) {  
  15.             device->classes |= INPUT_DEVICE_CLASS_KEYBOARD;  
  16.   
  17.             device->keyBitmask = new uint8_t[sizeof(key_bitmask)];  
  18.             if (device->keyBitmask != NULL) {  
  19.                 memcpy(device->keyBitmask, key_bitmask, sizeof(key_bitmask));  
  20.             } else {  
  21.                 delete device;  
  22.                 LOGE("out of memory allocating key bitmask");  
  23.                 return -1;  
  24.             }  
  25.     }  
  26. }  
        如果是的話,還要繼續進一步初始化前面爲這個設備文件所創建的device_t結構體,主要就是把結構體device的classes成員變量的INPUT_DEVICE_CLASS_KEYBOARD位置爲1了,以表明這是一個鍵盤。
        如果是鍵盤設備,初始化工作還未完成,還要繼續設置鍵盤的佈局等信息:
  1. if ((device->classes & INPUT_DEVICE_CLASS_KEYBOARD) != 0) {  
  2.     char tmpfn[sizeof(name)];  
  3.     char keylayoutFilename[300];  
  4.   
  5.     // a more descriptive name  
  6.     device->name = name;  
  7.   
  8.     // replace all the spaces with underscores  
  9.     strcpy(tmpfn, name);  
  10.     for (char *p = strchr(tmpfn, ' '); p && *p; p = strchr(tmpfn, ' '))  
  11.         *p = '_';  
  12.   
  13.     // find the .kl file we need for this device  
  14.     const char* root = getenv("ANDROID_ROOT");  
  15.     snprintf(keylayoutFilename, sizeof(keylayoutFilename),  
  16.         "%s/usr/keylayout/%s.kl", root, tmpfn);  
  17.     bool defaultKeymap = false;  
  18.     if (access(keylayoutFilename, R_OK)) {  
  19.         snprintf(keylayoutFilename, sizeof(keylayoutFilename),  
  20.             "%s/usr/keylayout/%s", root, "qwerty.kl");  
  21.         defaultKeymap = true;  
  22.     }  
  23.     status_t status = device->layoutMap->load(keylayoutFilename);  
  24.     if (status) {  
  25.         LOGE("Error %d loading key layout.", status);  
  26.     }  
  27.   
  28.     // tell the world about the devname (the descriptive name)  
  29.     if (!mHaveFirstKeyboard && !defaultKeymap && strstr(name, "-keypad")) {  
  30.         // the built-in keyboard has a well-known device ID of 0,  
  31.         // this device better not go away.  
  32.         mHaveFirstKeyboard = true;  
  33.         mFirstKeyboardId = device->id;  
  34.         property_set("hw.keyboards.0.devname", name);  
  35.     } else {  
  36.         // ensure mFirstKeyboardId is set to -something-.  
  37.         if (mFirstKeyboardId == 0) {  
  38.             mFirstKeyboardId = device->id;  
  39.         }  
  40.     }  
  41.     char propName[100];  
  42.     sprintf(propName, "hw.keyboards.%u.devname", device->id);  
  43.     property_set(propName, name);  
  44.   
  45.     // 'Q' key support = cheap test of whether this is an alpha-capable kbd  
  46.     if (hasKeycodeLocked(device, AKEYCODE_Q)) {  
  47.         device->classes |= INPUT_DEVICE_CLASS_ALPHAKEY;  
  48.     }  
  49.   
  50.     // See if this device has a DPAD.  
  51.     if (hasKeycodeLocked(device, AKEYCODE_DPAD_UP) &&  
  52.         hasKeycodeLocked(device, AKEYCODE_DPAD_DOWN) &&  
  53.         hasKeycodeLocked(device, AKEYCODE_DPAD_LEFT) &&  
  54.         hasKeycodeLocked(device, AKEYCODE_DPAD_RIGHT) &&  
  55.         hasKeycodeLocked(device, AKEYCODE_DPAD_CENTER)) {  
  56.             device->classes |= INPUT_DEVICE_CLASS_DPAD;  
  57.     }  
  58.   
  59.     // See if this device has a gamepad.  
  60.     for (size_t i = 0; i < sizeof(GAMEPAD_KEYCODES)/sizeof(GAMEPAD_KEYCODES[0]); i++) {  
  61.         if (hasKeycodeLocked(device, GAMEPAD_KEYCODES[i])) {  
  62.             device->classes |= INPUT_DEVICE_CLASS_GAMEPAD;  
  63.             break;  
  64.         }  
  65.     }  
  66.   
  67.     LOGI("New keyboard: device->id=0x%x devname='%s' propName='%s' keylayout='%s'\n",  
  68.         device->id, name, propName, keylayoutFilename);  
  69. }  
        到這裏,系統中的輸入設備文件就打開了。

        回到Step 18中,我們繼續分析EventHub.getEvent函數的實現。

        在中間的for循環裏面,首先會檢查當前是否有輸入設備被關閉,如果有,就返回一個設備移除的事件給調用方:

  1. // Report any devices that had last been added/removed.  
  2. if (mClosingDevices != NULL) {  
  3.     device_t* device = mClosingDevices;  
  4.     LOGV("Reporting device closed: id=0x%x, name=%s\n",  
  5.         device->id, device->path.string());  
  6.     mClosingDevices = device->next;  
  7.     if (device->id == mFirstKeyboardId) {  
  8.         outEvent->deviceId = 0;  
  9.     } else {  
  10.         outEvent->deviceId = device->id;  
  11.     }  
  12.     outEvent->type = DEVICE_REMOVED;  
  13.     outEvent->when = systemTime(SYSTEM_TIME_MONOTONIC);  
  14.     delete device;  
  15.     mNeedToSendFinishedDeviceScan = true;  
  16.     return true;  
  17. }  
        接着,檢查當前是否有新的輸入設備加入進來:
  1. if (mOpeningDevices != NULL) {  
  2.     device_t* device = mOpeningDevices;  
  3.     LOGV("Reporting device opened: id=0x%x, name=%s\n",  
  4.         device->id, device->path.string());  
  5.     mOpeningDevices = device->next;  
  6.     if (device->id == mFirstKeyboardId) {  
  7.         outEvent->deviceId = 0;  
  8.     } else {  
  9.         outEvent->deviceId = device->id;  
  10.     }  
  11.     outEvent->type = DEVICE_ADDED;  
  12.     outEvent->when = systemTime(SYSTEM_TIME_MONOTONIC);  
  13.     mNeedToSendFinishedDeviceScan = true;  
  14.     return true;  
  15. }  
        接着,再檢查是否需要結束監控輸入事件:
  1. if (mNeedToSendFinishedDeviceScan) {  
  2.     mNeedToSendFinishedDeviceScan = false;  
  3.     outEvent->type = FINISHED_DEVICE_SCAN;  
  4.     outEvent->when = systemTime(SYSTEM_TIME_MONOTONIC);  
  5.     return true;  
  6. }  
        最後,就是要檢查當前是否有還未處理的輸入設備事件發生了:
  1. // Grab the next input event.  
  2. for (;;) {  
  3.     // Consume buffered input events, if any.  
  4.     if (mInputBufferIndex < mInputBufferCount) {  
  5.         const struct input_event& iev = mInputBufferData[mInputBufferIndex++];  
  6.         const device_t* device = mDevices[mInputDeviceIndex];  
  7.   
  8.         LOGV("%s got: t0=%d, t1=%d, type=%d, code=%d, v=%d", device->path.string(),  
  9.             (int) iev.time.tv_sec, (int) iev.time.tv_usec, iev.type, iev.code, iev.value);  
  10.         if (device->id == mFirstKeyboardId) {  
  11.             outEvent->deviceId = 0;  
  12.         } else {  
  13.             outEvent->deviceId = device->id;  
  14.         }  
  15.         outEvent->type = iev.type;  
  16.         outEvent->scanCode = iev.code;  
  17.         if (iev.type == EV_KEY) {  
  18.             status_t err = device->layoutMap->map(iev.code,  
  19.                 & outEvent->keyCode, & outEvent->flags);  
  20.             LOGV("iev.code=%d keyCode=%d flags=0x%08x err=%d\n",  
  21.                 iev.code, outEvent->keyCode, outEvent->flags, err);  
  22.             if (err != 0) {  
  23.                 outEvent->keyCode = AKEYCODE_UNKNOWN;  
  24.                 outEvent->flags = 0;  
  25.             }  
  26.         } else {  
  27.             outEvent->keyCode = iev.code;  
  28.         }  
  29.         outEvent->value = iev.value;  
  30.   
  31.         // Use an event timestamp in the same timebase as  
  32.         // java.lang.System.nanoTime() and android.os.SystemClock.uptimeMillis()  
  33.         // as expected by the rest of the system.  
  34.         outEvent->when = systemTime(SYSTEM_TIME_MONOTONIC);  
  35.         return true;  
  36.     }  
  37.   
  38.     // Finish reading all events from devices identified in previous poll().  
  39.     // This code assumes that mInputDeviceIndex is initially 0 and that the  
  40.     // revents member of pollfd is initialized to 0 when the device is first added.  
  41.     // Since mFDs[0] is used for inotify, we process regular events starting at index 1.  
  42.     mInputDeviceIndex += 1;  
  43.     if (mInputDeviceIndex >= mFDCount) {  
  44.         break;  
  45.     }  
  46.   
  47.     const struct pollfd& pfd = mFDs[mInputDeviceIndex];  
  48.     if (pfd.revents & POLLIN) {  
  49.         int32_t readSize = read(pfd.fd, mInputBufferData,  
  50.             sizeof(struct input_event) * INPUT_BUFFER_SIZE);  
  51.         if (readSize < 0) {  
  52.             if (errno != EAGAIN && errno != EINTR) {  
  53.                 LOGW("could not get event (errno=%d)", errno);  
  54.             }  
  55.         } else if ((readSize % sizeof(struct input_event)) != 0) {  
  56.             LOGE("could not get event (wrong size: %d)", readSize);  
  57.         } else {  
  58.             mInputBufferCount = readSize / sizeof(struct input_event);  
  59.             mInputBufferIndex = 0;  
  60.         }  
  61.     }  
  62. }  
        未處理的輸入事件保存在成員變量mInputBufferData中,如果有的話,就可以直接返回了,否則的話,就要通過系統調用poll來等待輸入設備上發生新的事件了,在我們這個場景中,就是等待鍵盤有鍵被按下或者鬆開了。:
  1. int pollResult = poll(mFDs, mFDCount, -1);  

        這裏的mFDs包含了我們所要監控的輸入設備的打開文件描述符,這是在前面的openPlatformInput函數中初始化的。

        Step 22. poll

        這是一個Linux系統的文件操作系統調用,它用來查詢指定的文件列表是否有有可讀寫的,如果有,就馬上返回,否則的話,就阻塞線程,並等待驅動程序喚醒,重新調用poll函數,或超時返回。在我們的這個場景中,就是要查詢是否有鍵盤事件發生,如果有的話,就返回,否則的話,當前線程就睡眠等待鍵盤事件的發生了。

        這樣,InputManager的啓動過程就分析完了,下面我們再分析應用程序註冊鍵盤消息接收通道的過程。

        2. 應用程序註冊鍵盤消息接收通道的過程分析

        InputManager啓動以後,就開始負責監控鍵盤輸入事件了。當InputManager監控到鍵盤輸入事件時,它應該把這個鍵盤事件分發給誰呢?當然是要把這個鍵盤消息分發給當前激活的Activity窗口了,不過,當前激活的Activity窗口還需要主動註冊一個鍵盤消息接收通道到InputManager中去,InputManager才能把這個鍵盤消息分發給它處理。那麼,當前被激活的Activity窗口又是什麼時候去註冊這個鍵盤消息接收通道的呢?在前面一篇文章Android應用程序啓動過程源代碼分析中,我們分析Android應用程序的啓動過程時,在Step 33中分析到ActivityThread類的handleLaunchActivity函數中,我們曾經說過,當函數handleLaunchActivity調用performLaunchActivity函數來加載這個完畢應用程序的默認Activity後,再次回到handleLaunchActivity函數時,會調用handleResumeActivity函數來使這個Activity進入Resumed狀態。在調用handleResumeActivity函數的過程中,ActivityThread會通過android.view.WindowManagerImpl類爲該Activity創建一個ViewRoot實例,並且會通過調用ViewRoot類的setView成員函數把與該Activity關聯的View設置到這個ViewRoot中去,而Activity正是通過ViewRoot類的setView成員函數來註冊鍵盤消息接收通道的。

        有了這些背影知識後,接下來,我們就可以從ViewRoot.setView函數開始分析應用程序註冊鍵盤消息接收通道的過程了。首先看一下這個註冊過程的序列圖,然後再詳細分析每一個步驟:


點擊查看大圖

        Step 1. ViewRoot.setView

        這個函數定義在frameworks/base/core/java/android/view/ViewRoot.java文件中:

  1. public final class ViewRoot extends Handler implements ViewParent,  
  2.         View.AttachInfo.Callbacks {  
  3.     ......  
  4.   
  5.     public void setView(View view, WindowManager.LayoutParams attrs,  
  6.             View panelParentView) {  
  7.         ......  
  8.   
  9.         synchronized (this) {  
  10.             if (mView == null) {  
  11.                 ......  
  12.   
  13.                 // Schedule the first layout -before- adding to the window  
  14.                 // manager, to make sure we do the relayout before receiving  
  15.                 // any other events from the system.  
  16.                 requestLayout();  
  17.                 mInputChannel = new InputChannel();  
  18.                 try {  
  19.                     res = sWindowSession.add(mWindow, mWindowAttributes,  
  20.                         getHostVisibility(), mAttachInfo.mContentInsets,  
  21.                         mInputChannel);  
  22.                 } catch (RemoteException e) {  
  23.                     ......  
  24.                 } finally {  
  25.                     ......  
  26.                 }  
  27.   
  28.                 ......  
  29.   
  30.                 if (view instanceof RootViewSurfaceTaker) {  
  31.                     mInputQueueCallback =  
  32.                         ((RootViewSurfaceTaker)view).willYouTakeTheInputQueue();  
  33.                 }  
  34.                 if (mInputQueueCallback != null) {  
  35.                     mInputQueue = new InputQueue(mInputChannel);  
  36.                     mInputQueueCallback.onInputQueueCreated(mInputQueue);  
  37.                 } else {  
  38.                     InputQueue.registerInputChannel(mInputChannel, mInputHandler,  
  39.                         Looper.myQueue());  
  40.                 }  
  41.   
  42.                 ......  
  43.             }  
  44.         }  
  45.     }  
  46.   
  47. }  

        這個函數中與註冊鍵盤消息接收通道(InputChannel)相關的邏輯主要有三處,一是調用requestLayout函數來通知InputManager,這個Activity窗口是當前被激活的窗口,二是調用sWindowSession(WindowManagerService內部類Session的遠程接口)的add成員函數來把鍵盤消息接收通道的一端註冊在InputManager中,三是調用InputQueue的registerInputChannel成員函數來把鍵盤消息接收通道的另一端註冊在本應用程序的消息循環(Looper)中。這樣,當InputManager監控到有鍵盤消息時,就會先找到當前被激活的窗口,然後找到其在InputManager中對應的鍵盤消息接收通道,通過這個通道在InputManager中的一端來通知在應用程序消息循環中的另一端,就把鍵盤消息分發給當前激活的Activity窗口了。

        在接下來的內容中,我們首先描述requestLayout函數是如何告訴InputManager當前的Activity窗口便是激活窗口的,接着再回過頭來分析應用程序是如何把鍵盤消息接收通道的一端註冊到InputManager中去的,最後分析應用程序是如何鍵盤消息接收通道的另一端註冊到本應用程序的消息循環中去了。

        Step 2. ViewRoot.requestLayout

        這個函數定義在frameworks/base/core/java/android/view/ViewRoot.java文件中:

  1. public final class ViewRoot extends Handler implements ViewParent,  
  2.         View.AttachInfo.Callbacks {  
  3.     ......  
  4.   
  5.     public void requestLayout() {  
  6.         ......  
  7.         mLayoutRequested = true;  
  8.         scheduleTraversals();  
  9.     }  
  10.   
  11.     ......  
  12. }  
        這個函數調用了scheduleTraversals函數來進一步執行操作,由於篇幅關係,我們就不詳細描述scheduleTraversals函數了,簡單來說,在scheduleTraversals函數中,會通過sendEmptyMessage(DO_TRAVERSAL)發送一個消息到應用程序的消息隊列中,這個消息最終由ViewRoot的handleMessage函數處理,而ViewRoot的handleMessage函數把這個消息交給ViewRoot類的performTraversals來處理,在performTraversals函數中,又會調用ViewRoot類的relayoutWindow函數來進一步執行操作,最後在relayoutWindow函數中,就會通過WindowManagerService內部類Session的遠程接口sWindowSession的relayout函數來進入到WindowManagerService中。

        Step 3. WindowManagerService.Session.relayout

        這個函數定義在frameworks/base/services/java/com/android/server/WindowManagerService.java 文件中:

  1. public class WindowManagerService extends IWindowManager.Stub  
  2.         implements Watchdog.Monitor {  
  3.     ......  
  4.   
  5.     private final class Session extends IWindowSession.Stub  
  6.             implements IBinder.DeathRecipient {  
  7.         ......  
  8.   
  9.         public int relayout(IWindow window, WindowManager.LayoutParams attrs,  
  10.                 int requestedWidth, int requestedHeight, int viewFlags,  
  11.                 boolean insetsPending, Rect outFrame, Rect outContentInsets,  
  12.                 Rect outVisibleInsets, Configuration outConfig, Surface outSurface) {  
  13.             //Log.d(TAG, ">>>>>> ENTERED relayout from " + Binder.getCallingPid());  
  14.             int res = relayoutWindow(this, window, attrs,  
  15.                     requestedWidth, requestedHeight, viewFlags, insetsPending,  
  16.                     outFrame, outContentInsets, outVisibleInsets, outConfig, outSurface);  
  17.             //Log.d(TAG, "<<<<<< EXITING relayout to " + Binder.getCallingPid());  
  18.             return res;  
  19.         }  
  20.   
  21.         ......  
  22.     }  
  23.   
  24.     ......  
  25. }  

        這個函數只是簡單地調用WindowManagerService的成員函數relayoutWIndow來進一步處理。

        Step 4. WindowManagerService.relayoutWIndow

        這個函數定義在frameworks/base/services/java/com/android/server/WindowManagerService.java 文件中:

  1. public class WindowManagerService extends IWindowManager.Stub  
  2.         implements Watchdog.Monitor {  
  3.     ......  
  4.   
  5.     public int relayoutWindow(Session session, IWindow client,  
  6.             WindowManager.LayoutParams attrs, int requestedWidth,  
  7.             int requestedHeight, int viewVisibility, boolean insetsPending,  
  8.             Rect outFrame, Rect outContentInsets, Rect outVisibleInsets,  
  9.             Configuration outConfig, Surface outSurface) {  
  10.         ......  
  11.   
  12.         synchronized(mWindowMap) {  
  13.             ......  
  14.   
  15.             mInputMonitor.updateInputWindowsLw();  
  16.         }  
  17.   
  18.         ......  
  19.     }  
  20.   
  21.     ......  
  22. }  
         這個函數又會繼續調用mInputMonitor的updateInputWindowsLw成員函數來更新當前的輸入窗口,mInputMonitor是WindowManagerService的成員變量,它的類型爲InputMonitor。

         Step 5. InputMonitor.updateInputWindowsLw

         這個函數定義在frameworks/base/services/java/com/android/server/WindowManagerService.java 文件中:

  1. public class WindowManagerService extends IWindowManager.Stub  
  2.         implements Watchdog.Monitor {  
  3.     ......  
  4.   
  5.     final class InputMonitor {  
  6.         ......  
  7.   
  8.         /* Updates the cached window information provided to the input dispatcher. */  
  9.         public void updateInputWindowsLw() {  
  10.             // Populate the input window list with information about all of the windows that  
  11.             // could potentially receive input.  
  12.             // As an optimization, we could try to prune the list of windows but this turns  
  13.             // out to be difficult because only the native code knows for sure which window  
  14.             // currently has touch focus.  
  15.             final ArrayList<WindowState> windows = mWindows;  
  16.             final int N = windows.size();  
  17.             for (int i = N - 1; i >= 0; i--) {  
  18.                 final WindowState child = windows.get(i);  
  19.                 if (child.mInputChannel == null || child.mRemoved) {  
  20.                     // Skip this window because it cannot possibly receive input.  
  21.                     continue;  
  22.                 }  
  23.   
  24.                 ......  
  25.   
  26.                 // Add a window to our list of input windows.  
  27.                 final InputWindow inputWindow = mTempInputWindows.add();  
  28.   
  29.                 ......  
  30.             }  
  31.   
  32.             // Send windows to native code.  
  33.             mInputManager.setInputWindows(mTempInputWindows.toNullTerminatedArray());  
  34.   
  35.             ......  
  36.         }  
  37.   
  38.         ......  
  39.     }  
  40.   
  41.     ......  
  42. }  
        這個函數將當前系統中帶有InputChannel的Activity窗口都設置爲InputManager的輸入窗口,但是後面我們會看到,只有當前激活的窗口才會響應鍵盤消息。

        Step 6. InputManager.setInputWindows

        這個函數定義在frameworks/base/services/java/com/android/server/InputManager.java文件中:

  1. public class InputManager {  
  2.     ......  
  3.   
  4.     public void setInputWindows(InputWindow[] windows) {  
  5.         nativeSetInputWindows(windows);  
  6.     }  
  7.   
  8.     ......  
  9. }  
        這個函數調用了本地方法nativeSetInputWindows來進一步執行操作。
        Step 7. InputManager.nativeSetInputWindows

        這個函數定義在frameworks/base/services/jni/com_android_server_InputManager.cpp 文件中:

  1. static void android_server_InputManager_nativeSetInputWindows(JNIEnv* env, jclass clazz,  
  2.         jobjectArray windowObjArray) {  
  3.     if (checkInputManagerUnitialized(env)) {  
  4.         return;  
  5.     }  
  6.   
  7.     gNativeInputManager->setInputWindows(env, windowObjArray);  
  8. }  
        這裏的gNativeInputManager我們前面分析InputManager的啓動過程時已經見過了,這是一個本地InputManager對象,通過它進一步設置當前系統的輸入窗口。

        Step 8. NativeInputManager.setInputWindows

        這個函數定義在frameworks/base/services/jni/com_android_server_InputManager.cpp 文件中:

  1. void NativeInputManager::setInputWindows(JNIEnv* env, jobjectArray windowObjArray) {  
  2.     Vector<InputWindow> windows;  
  3.   
  4.     jsize length = env->GetArrayLength(windowObjArray);  
  5.     for (jsize i = 0; i < length; i++) {  
  6.         jobject inputTargetObj = env->GetObjectArrayElement(windowObjArray, i);  
  7.         if (! inputTargetObj) {  
  8.             break// found null element indicating end of used portion of the array  
  9.         }  
  10.   
  11.         windows.push();  
  12.         InputWindow& window = windows.editTop();  
  13.         bool valid = populateWindow(env, inputTargetObj, window);  
  14.         if (! valid) {  
  15.             windows.pop();  
  16.         }  
  17.   
  18.         env->DeleteLocalRef(inputTargetObj);  
  19.     }  
  20.   
  21.     mInputManager->getDispatcher()->setInputWindows(windows);  
  22. }  
        這個函數首先將Java層的Window轉換成C++層的InputWindow,然後放在windows向量中,最後將這些輸入窗口設置到InputDispatcher中去。

        Step 9. InputDispatcher.setInputWindows

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

  1. void InputDispatcher::setInputWindows(const Vector<InputWindow>& inputWindows) {  
  2.     ......  
  3.   
  4.     { // acquire lock  
  5.         AutoMutex _l(mLock);  
  6.   
  7.         // Clear old window pointers.  
  8.         sp<InputChannel> oldFocusedWindowChannel;  
  9.         if (mFocusedWindow) {  
  10.             oldFocusedWindowChannel = mFocusedWindow->inputChannel;  
  11.             mFocusedWindow = NULL;  
  12.         }  
  13.   
  14.         mWindows.clear();  
  15.   
  16.         // Loop over new windows and rebuild the necessary window pointers for  
  17.         // tracking focus and touch.  
  18.         mWindows.appendVector(inputWindows);  
  19.   
  20.         size_t numWindows = mWindows.size();  
  21.         for (size_t i = 0; i < numWindows; i++) {  
  22.             const InputWindow* window = & mWindows.itemAt(i);  
  23.             if (window->hasFocus) {  
  24.                 mFocusedWindow = window;  
  25.                 break;  
  26.             }  
  27.         }  
  28.   
  29.         ......  
  30.   
  31.     } // release lock  
  32.   
  33.     ......  
  34. }  
        這裏InputDispatcher的成員變量mFocusedWindow就代表當前激活的窗口的。這個函數首先清空mFocusedWindow,然後再通過一個for循環檢查當前的輸入窗口中的哪一個窗口是獲得焦點的,獲得焦點的輸入窗口即爲當前激活的窗口。

        這樣,InputManager就把當前激活的Activity窗口保存在InputDispatcher中了,後面就可以把鍵盤消息分發給它來處理。

        回到Step 1中的ViewRoot.setView函數中,接下來就調用下面語句來註冊鍵盤消息接收通道的一端到InputManager中去:

  1. mInputChannel = new InputChannel();  
  2. try {  
  3.     res = sWindowSession.add(mWindow, mWindowAttributes,  
  4.             getHostVisibility(), mAttachInfo.mContentInsets,  
  5.             mInputChannel);  
  6. catch (RemoteException e) {  
  7.     ......  
  8. finally {  
  9.     ......  
  10. }  
        前面說過,這裏的sWindowSession是WindowManagerService內部類Session的一個遠程接口,通過它可以進入到WindowManagerService中去。

        Step 10. WindowManagerService.Session.add

        這個函數定義在frameworks/base/services/java/com/android/server/WindowManagerService.java 文件中:

  1. public class WindowManagerService extends IWindowManager.Stub  
  2.         implements Watchdog.Monitor {  
  3.     ......  
  4.   
  5.     private final class Session extends IWindowSession.Stub  
  6.             implements IBinder.DeathRecipient {  
  7.         ......  
  8.   
  9.         public int add(IWindow window, WindowManager.LayoutParams attrs,  
  10.                 int viewVisibility, Rect outContentInsets, InputChannel outInputChannel) {  
  11.             return addWindow(this, window, attrs, viewVisibility, outContentInsets,  
  12.                 outInputChannel);  
  13.         }  
  14.   
  15.         ......  
  16.     }  
  17.   
  18.     ......  
  19. }  
        這裏調用WindowManagerService類的addWindow函數來進一步執行操作。

        Step 11. WindowManagerService.addWindow

        這個函數定義在frameworks/base/services/java/com/android/server/WindowManagerService.java 文件中:

  1. public class WindowManagerService extends IWindowManager.Stub  
  2.         implements Watchdog.Monitor {  
  3.     ......  
  4.   
  5.     public int addWindow(Session session, IWindow client,  
  6.             WindowManager.LayoutParams attrs, int viewVisibility,  
  7.             Rect outContentInsets, InputChannel outInputChannel) {  
  8.         ......  
  9.   
  10.         WindowState win = null;  
  11.   
  12.         synchronized(mWindowMap) {  
  13.             ......  
  14.   
  15.             win = new WindowState(session, client, token,  
  16.                 attachedWindow, attrs, viewVisibility);  
  17.   
  18.             ......  
  19.   
  20.             if (outInputChannel != null) {  
  21.                 String name = win.makeInputChannelName();  
  22.                 InputChannel[] inputChannels = InputChannel.openInputChannelPair(name);  
  23.                 win.mInputChannel = inputChannels[0];  
  24.                 inputChannels[1].transferToBinderOutParameter(outInputChannel);  
  25.                 mInputManager.registerInputChannel(win.mInputChannel);  
  26.             }  
  27.   
  28.             ......  
  29.         }  
  30.   
  31.         ......  
  32.     }  
  33.   
  34.     ......  
  35. }  
        這裏的outInputChannel即爲前面在Step 1中創建的InputChannel,它不爲NULL,因此,這裏會通過InputChannel.openInputChannelPair函數來創建一對輸入通道,其中一個位於WindowManagerService中,另外一個通過outInputChannel參數返回到應用程序中:
  1. inputChannels[1].transferToBinderOutParameter(outInputChannel);  

        創建輸入通道之前,WindowManagerService會爲當前Activity窗口創建一個WindowState對象win,用來記錄這個Activity窗口的狀態信息。當創建這對輸入管道成功以後,也會把其中的一個管道保存在這個WindowState對象win的成員變量mInputChannel中,後面要註銷這個管道的時候,就是從這個WindownState對象中取回這個管道的:

  1. win.mInputChannel = inputChannels[0];  

        接下來我們就看一下InputChannel.openInputChannelPair函數的實現。

        Step 12. InputChannel.openInputChannelPair

        這個函數定義在frameworks/base/core/java/android/view/InputChannel.java文件中:

  1. public final class InputChannel implements Parcelable {  
  2.     ......  
  3.   
  4.     /** 
  5.     * Creates a new input channel pair.  One channel should be provided to the input 
  6.     * dispatcher and the other to the application's input queue. 
  7.     * @param name The descriptive (non-unique) name of the channel pair. 
  8.     * @return A pair of input channels.  They are symmetric and indistinguishable. 
  9.     */  
  10.     public static InputChannel[] openInputChannelPair(String name) {  
  11.         ......  
  12.   
  13.         return nativeOpenInputChannelPair(name);  
  14.     }  
  15.   
  16.     ......  
  17. }  
         這個函數調用本地方法nativeOpenInputChannelPair來進一步執行操作。

         Step 13. InputChannel.nativeOpenInputChannelPair
         這個函數定義在frameworks/base/core/jni/android_view_InputChannel.cpp文件中:

  1. static jobjectArray android_view_InputChannel_nativeOpenInputChannelPair(JNIEnv* env,  
  2.         jclass clazz, jstring nameObj) {  
  3.      const char* nameChars = env->GetStringUTFChars(nameObj, NULL);  
  4.      String8 name(nameChars);  
  5.      env->ReleaseStringUTFChars(nameObj, nameChars);  
  6.   
  7.      sp<InputChannel> serverChannel;  
  8.      sp<InputChannel> clientChannel;  
  9.      status_t result = InputChannel::openInputChannelPair(name, serverChannel, clientChannel);  
  10.   
  11.      if (result) {  
  12.          LOGE("Could not open input channel pair.  status=%d", result);  
  13.          jniThrowRuntimeException(env, "Could not open input channel pair.");  
  14.          return NULL;  
  15.      }  
  16.   
  17.      // TODO more robust error checking  
  18.      jobject serverChannelObj = android_view_InputChannel_createInputChannel(env,  
  19.          new NativeInputChannel(serverChannel));  
  20.      jobject clientChannelObj = android_view_InputChannel_createInputChannel(env,  
  21.          new NativeInputChannel(clientChannel));  
  22.   
  23.      jobjectArray channelPair = env->NewObjectArray(2, gInputChannelClassInfo.clazz, NULL);  
  24.      env->SetObjectArrayElement(channelPair, 0, serverChannelObj);  
  25.      env->SetObjectArrayElement(channelPair, 1, clientChannelObj);  
  26.      return channelPair;  
  27. }  
        這個函數根據傳進來的參數name在C++層分別創建兩個InputChannel,一個作爲Server端使用,一個作爲Client端使用,這裏的Server端即是指InputManager,而Client端即是指應用程序。這兩個本地的InputChannel是通過InputChannel::openInputChannelPair函數創建的,創建完成後,再相應地在Java層創建相應的兩個InputChannel,然後返回。

        Step 14. InputChannel.openInputChannelPair
        這個函數定義在frameworks/base/libs/ui/InputTransport.cpp文件中:

  1. status_t InputChannel::openInputChannelPair(const String8& name,  
  2.         sp<InputChannel>& outServerChannel, sp<InputChannel>& outClientChannel) {  
  3.     status_t result;  
  4.   
  5.     int serverAshmemFd = ashmem_create_region(name.string(), DEFAULT_MESSAGE_BUFFER_SIZE);  
  6.     if (serverAshmemFd < 0) {  
  7.         ......  
  8.     } else {  
  9.         result = ashmem_set_prot_region(serverAshmemFd, PROT_READ | PROT_WRITE);  
  10.         if (result < 0) {  
  11.             ......  
  12.         } else {  
  13.             // Dup the file descriptor because the server and client input channel objects that  
  14.             // are returned may have different lifetimes but they share the same shared memory region.  
  15.             int clientAshmemFd;  
  16.             clientAshmemFd = dup(serverAshmemFd);  
  17.             if (clientAshmemFd < 0) {  
  18.                 ......  
  19.             } else {  
  20.                 int forward[2];  
  21.                 if (pipe(forward)) {  
  22.                     ......  
  23.                 } else {  
  24.                     int reverse[2];  
  25.                     if (pipe(reverse)) {  
  26.                         ......  
  27.                     } else {  
  28.                         String8 serverChannelName = name;  
  29.                         serverChannelName.append(" (server)");  
  30.                         outServerChannel = new InputChannel(serverChannelName,  
  31.                             serverAshmemFd, reverse[0], forward[1]);  
  32.   
  33.                         String8 clientChannelName = name;  
  34.                         clientChannelName.append(" (client)");  
  35.                         outClientChannel = new InputChannel(clientChannelName,  
  36.                             clientAshmemFd, forward[0], reverse[1]);  
  37.                         return OK;  
  38.                     }  
  39.                     ......  
  40.                 }  
  41.                 ......  
  42.             }  
  43.         }  
  44.     }  
  45.     ......  
  46. }  
        在閱讀這個函數之前,我們首先了解一下C++層的InputChannel的構造函數:
  1. InputChannel::InputChannel(const String8& name, int32_t ashmemFd, int32_t receivePipeFd,  
  2.     int32_t sendPipeFd) :  
  3.     mName(name), mAshmemFd(ashmemFd), mReceivePipeFd(receivePipeFd), mSendPipeFd(sendPipeFd) {  
  4.     ......  
  5. }   
        爲了創建一個InputChannel,我們需要準備四個參數,一個是輸入通道的名稱name,一個是匿名共享內存文件描述符,一個是管道的讀端文件描述符,一個是管道的寫端文件描述符。在上面的openInputChannelPair函數,輸入通道的名稱已經作爲參數傳遞進來,因此,還需要創建匿名共享內存文件,還有管道。這裏需要創建兩個管道,一個稱爲前向管道(forward pipe),一個稱爲反向管道(reverse pipe),它們交叉使用在Server端和Client端的InputChannel中,這樣就使入Server和Client可以互相通信了。

        具體來說,Server端和Client端的InputChannel分別是這樣構成的:

        Server Input Channel:  ashmem - reverse(read) - forward(write)

        Client Input Channel:   ashmem - forward(read) - reverse(write)
        前面我們在Android應用程序消息處理機制(Looper、Handler)分析一文中學習Android應用程序的消息處理機制時知道,管道可以用作進程間通信,其中一個進程在管道的讀端等待新的內空可讀,另一個進程在管道的寫端寫入新的內容以喚醒在管道讀端等待的進程,這樣就實現了進程間通信。在我們這個情景中,Client端可以在前向管道(forward pipe)的讀端睡眠等待新的內容可讀,而Server端可以通過向前向管道(forward pipe)的寫端寫入新的內容來喚醒Client端,同樣,把前向管道(forward pipe)換成反向管道(reverse pipe),也能實現Client端喚醒Server端。在後面我們分析InputDispatcher分發鍵盤消息時,會看到它們的用法。

        有了這些背景知識後,相信上面的openInputChannelPair的代碼就容易理解了,這裏就不再詳述了。

        創建好了這兩個輸入通道後,回到Step 11中的WindowManagerService.addWindow函數中,一方面它把剛纔創建的Client端的輸入通道通過outInputChannel參數返回到應用程序中:

  1. inputChannels[1].transferToBinderOutParameter(outInputChannel);  

       另一方面,它還要把剛纔創建的Server端的輸入通道註冊到InputManager中:

  1. mInputManager.registerInputChannel(win.mInputChannel);  
       Step 15. InputManager.registerInputChannel

       這個函數定義在frameworks/base/services/java/com/android/server/InputManager.java文件中:

  1. public class InputManager {  
  2.     ......  
  3.   
  4.     /** 
  5.     * Registers an input channel so that it can be used as an input event target. 
  6.     * @param inputChannel The input channel to register. 
  7.     */  
  8.     public void registerInputChannel(InputChannel inputChannel) {  
  9.         if (inputChannel == null) {  
  10.             throw new IllegalArgumentException("inputChannel must not be null.");  
  11.         }  
  12.   
  13.         nativeRegisterInputChannel(inputChannel, false);  
  14.     }  
  15.   
  16.     ......  
  17. }  
         它通過調用本地方法nativeRegisterInputChannel來執行進一步的操作。

         Step 16. InputManager.nativeRegisterInputChannel

         這個函數定義在frameworks/base/services/jni/com_android_server_InputManager.cpp 文件中:

  1. static void android_server_InputManager_nativeRegisterInputChannel(JNIEnv* env, jclass clazz,  
  2.         jobject inputChannelObj, jboolean monitor) {  
  3.     ......  
  4.   
  5.     sp<InputChannel> inputChannel = android_view_InputChannel_getInputChannel(env,  
  6.        inputChannelObj);  
  7.     ......  
  8.   
  9.     status_t status = gNativeInputManager->registerInputChannel(  
  10.        env, inputChannel, inputChannelObj, monitor);  
  11.       
  12.     ......  
  13. }  
        這裏首先通過Java層的InputChannel對象獲得C++層的InputChannel對象,它們之間的對應關係是在前面的Step 13中設置好的,接着調用NativeInputManager的registerInputChannel執行進一步的操作。

        Step 17. NativeInputManager.registerInputChannel

        這個函數定義在frameworks/base/services/jni/com_android_server_InputManager.cpp 文件中:

  1. status_t NativeInputManager::registerInputChannel(JNIEnv* env,  
  2.         const sp<InputChannel>& inputChannel, jobject inputChannelObj, bool monitor) {  
  3.     ......  
  4.   
  5.     status = mInputManager->getDispatcher()->registerInputChannel(inputChannel, monitor);  
  6.       
  7.     ......  
  8. }  
        這個函數主要是調用了InputDispatcher的registerInputChannel來真正執行註冊輸入通道的操作。

        Step 18. InputDispatcher.registerInputChannel
        這個函數定義在frameworks/base/libs/ui/InputDispatcher.cpp文件中:

  1. status_t InputDispatcher::registerInputChannel(const sp<InputChannel>& inputChannel, bool monitor) {  
  2.     ......  
  3.   
  4.     { // acquire lock  
  5.         AutoMutex _l(mLock);  
  6.   
  7.         if (getConnectionIndexLocked(inputChannel) >= 0) {  
  8.             LOGW("Attempted to register already registered input channel '%s'",  
  9.                 inputChannel->getName().string());  
  10.             return BAD_VALUE;  
  11.         }  
  12.   
  13.         sp<Connection> connection = new Connection(inputChannel);  
  14.         status_t status = connection->initialize();  
  15.         if (status) {  
  16.             LOGE("Failed to initialize input publisher for input channel '%s', status=%d",  
  17.                 inputChannel->getName().string(), status);  
  18.             return status;  
  19.         }  
  20.   
  21.         int32_t receiveFd = inputChannel->getReceivePipeFd();  
  22.         mConnectionsByReceiveFd.add(receiveFd, connection);  
  23.   
  24.         if (monitor) {  
  25.             mMonitoringChannels.push(inputChannel);  
  26.         }  
  27.   
  28.         mLooper->addFd(receiveFd, 0, ALOOPER_EVENT_INPUT, handleReceiveCallback, this);  
  29.   
  30.         runCommandsLockedInterruptible();  
  31.     } // release lock  
  32.     return OK;  
  33. }  
        這個函數首先會通過getConnectionIndexLocked檢查從參數傳進來的InputChannel是否已經註冊過了,如果已經註冊過了,就返回一個BAD_VALUE值了,否則的話,就會創建一個Connection對象來封裝即將要註冊的inputChannel,我們可以不關心這個Connection對象的實現,接着還通過調用inputChannel->getReceivePipeFd獲得一個管
道的讀端描述符。回憶一下Step 14中的InputChannel.openInputChannelPair函數,我們創建了一個Server端的InputChannel,就是對應這裏的inputChannel了,這個inputChannel的Receive Pipe Fd就是我們前面說的反向管道的讀端描述符了。有了這個Receive Pipe Fd後,就以它作爲Key值來把前面創建的Connection對象保存在InputDispatcher中,這樣就基本完成鍵盤消息接收通道的註冊了。但是,註冊的工作還未完成,最後,還要把這個Receive Pipe Fd添加到InputDispatcher的成員變量mLooper中去,這裏的成員變量mLooper的類型爲Looper,我們在前面介紹InputManager的啓動過程的Step 15中已經見過了,這裏就不再詳述了,不過這裏仍然值得介紹一下它的addFd函數。

        在前面一篇文章Android應用程序消息處理機制(Looper、Handler)分析中,我們在介紹到Android應用程序的消息循環一節時,曾經說過,在Looper類內部,會創建一個管道,然後Looper會睡眠在這個管道的讀端,等待另外一個線程來往這個管道的寫端寫入新的內容,從而喚醒等待在這個管道讀端的線程,除此之外,Looper還可以同時睡眠等待在其它的文件描述符上,因爲它是通過Linux系統的epoll機制來批量等待指定的文件有新的內容可讀的。這些其它的文件描述符就是通過Looper類的addFd成函數添加進去的了,在添加的時候,還可以指定回調函數,即當這個文件描述符所指向的文件有新的內容可讀時,Looper就會調用這個hanldeReceiveCallback函數,有興趣的讀者可以自己研究一下Looper類的addFd函數的實現,它位於frameworks/base/libs/utils/Looper.cpp文件中。

        分析到這裏,Server端的InputChannel就註冊完成了。回憶一下前面介紹InputManager啓動過程的Step 14,這時InputDispatcherThread同時睡眠在InputDispatcher的成員變量mLooper內部的管道的讀端以及這裏的Server端InputChannel裏面的反向管道的讀端上,mLooper內部的管道的讀端等待鍵盤事件的發生而被喚醒,而Server端InputChannel裏面的反向管道的讀端等待Client端InputChannel裏面的反向管道的寫端被寫入新的內容而被喚醒。

        Server端的InputChannel註冊完成後,回到Step 11中的WindowManagerService.addWindow函數,接下來就是把Client端的InputChannel轉換成addWindow的參數outInputChannel中,然後返回到Step 1中的ViewRoot.setView函數中,繼續執行Client端的InputChannel的註冊過程,即爲應用程序這一側註冊鍵盤消息接收通道:

  1. if (view instanceof RootViewSurfaceTaker) {  
  2.     mInputQueueCallback =  
  3.         ((RootViewSurfaceTaker)view).willYouTakeTheInputQueue();  
  4. }  
  5. if (mInputQueueCallback != null) {  
  6.     mInputQueue = new InputQueue(mInputChannel);  
  7.     mInputQueueCallback.onInputQueueCreated(mInputQueue);  
  8. else {  
  9.     InputQueue.registerInputChannel(mInputChannel, mInputHandler,  
  10.         Looper.myQueue());  
  11. }  

        這裏的變量view一般不爲RootViewSurfaceTaker的實例,因此,最後會執行下面語句:

  1. InputQueue.registerInputChannel(mInputChannel, mInputHandler,  
  2.     Looper.myQueue());  
        它調用InputQueue的registerInputChannel函數爲應用程序註冊鍵盤消息接收通道,這裏的mInputChannel即爲我們在前面Step 14中創建的Client端的InputChannel;Looper.myQueue函數返回的便是應用程序主線程的消息隊列,具體可以參考前面一篇文章Android應用程序消息處理機制(Looper、Handler)分析;參數mInputHandler是一個回調對象,當有鍵盤事件發生時,這個mInputHandler的handleKey函數就會被調用,在後面的分析中,我們將會看到。

        Step 19. InputQueue.registerInputChannel

        這個函數定義在frameworks/base/core/java/android/view/InputQueue.java文件中:

  1. public final class InputQueue {  
  2.     ......  
  3.   
  4.     public static void registerInputChannel(InputChannel inputChannel, InputHandler inputHandler,  
  5.             MessageQueue messageQueue) {  
  6.         ......  
  7.   
  8.         synchronized (sLock) {  
  9.             ......  
  10.   
  11.             nativeRegisterInputChannel(inputChannel, inputHandler, messageQueue);  
  12.         }  
  13.     }  
  14.   
  15.     ......  
  16. }  
         這個函數調用本地方法nativeRegisterInputChannel函數來執行進一步的操作。

         Step 20. InputQueue.nativeRegisterInputChannel

         這個函數定義在frameworks/base/core/jni/android_view_InputQueue.cpp文件中:

  1. static void android_view_InputQueue_nativeRegisterInputChannel(JNIEnv* env, jclass clazz,  
  2.         jobject inputChannelObj, jobject inputHandlerObj, jobject messageQueueObj) {  
  3.     status_t status = gNativeInputQueue.registerInputChannel(  
  4.        env, inputChannelObj, inputHandlerObj, messageQueueObj);  
  5.   
  6.     ......  
  7. }  
        這裏繼續調用NativeInputQueue的registerInputChannel函數來執行真正的鍵盤消息接收通道的工作。

        Step 21. NativeInputQueue.registerInputChannel

        這個函數定義在frameworks/base/core/jni/android_view_InputQueue.cpp文件中:

  1. status_t NativeInputQueue::registerInputChannel(JNIEnv* env, jobject inputChannelObj,  
  2.         jobject inputHandlerObj, jobject messageQueueObj) {  
  3.     sp<InputChannel> inputChannel = android_view_InputChannel_getInputChannel(env,  
  4.         inputChannelObj);  
  5.     ......  
  6.   
  7.     sp<Looper> looper = android_os_MessageQueue_getLooper(env, messageQueueObj);  
  8.   
  9.     { // acquire lock  
  10.         AutoMutex _l(mLock);  
  11.   
  12.         if (getConnectionIndex(inputChannel) >= 0) {  
  13.             LOGW("Attempted to register already registered input channel '%s'",  
  14.                 inputChannel->getName().string());  
  15.             return BAD_VALUE;  
  16.         }  
  17.   
  18.         uint16_t connectionId = mNextConnectionId++;  
  19.         sp<Connection> connection = new Connection(connectionId, inputChannel, looper);  
  20.         status_t result = connection->inputConsumer.initialize();  
  21.         if (result) {  
  22.             LOGW("Failed to initialize input consumer for input channel '%s', status=%d",  
  23.                 inputChannel->getName().string(), result);  
  24.             return result;  
  25.         }  
  26.   
  27.         connection->inputHandlerObjGlobal = env->NewGlobalRef(inputHandlerObj);  
  28.   
  29.         int32_t receiveFd = inputChannel->getReceivePipeFd();  
  30.         mConnectionsByReceiveFd.add(receiveFd, connection);  
  31.   
  32.         looper->addFd(receiveFd, 0, ALOOPER_EVENT_INPUT, handleReceiveCallback, this);  
  33.     } // release lock  
  34.   
  35.     ......  
  36.     return OK;  
  37. }  
        這裏註冊應用程序的InputChannel的邏輯和前面介紹的Step 18中在InputDispatcher中註冊Server端的InputChannel是一樣的,所不同的是,這裏用的looper是應用程序主線程中的消息循環對象Looper,而添加到這個looper對象中的Receive Pipe Fd是前面在Step 14中創建的前向管道的讀端文件描述符,而使用的回調函數是NativeInputQueue的成員函數handleReceiveCallback。

        介紹到這裏,應用程序註冊鍵盤消息接收通道的過程就分析完成了。這個過程比較複雜,這裏小結一下:

        A. 即將會被激活的Activity窗口,會通知InputManager,它是當前激活的窗口,因此,一旦發生鍵盤事件的時候,InputManager就把這個鍵盤事件拋給這個Activity處理;

        B. 應用程序會爲這個Activity窗口和InputManager之間創建一個鍵盤消息接收通道,這個通道的一端由一個Server端的InputChannel構成,另一端由Client端的InputChannel構成,Server端的InputChannel註冊在由InputManager所管理的InputDispatcher中,而Client端的InputChannel註冊在由應用程序主線程的消息循環對象Looper中;

        C. 註冊在InputDispatcher中的InputChannel由一個反向管道的讀端和一個前向管道的寫端組成,而註冊在應用程序主線程的消息循環對象Looper中的InputChannel由這個前向管道的讀端和反向管道的寫端組成,這種交叉結構使得當有鍵盤事件發生時,InputDispatcher可以把這個事件通知給應用程序。

        應用程序註冊好鍵盤消息接收通道後,接下來就開始分析InputManager分發鍵盤消息給應用程序的過程了。

        3. InputManager分發鍵盤消息給應用程序的過程分析

        在分析InputManager分發鍵盤消息給應用程序的過程之前,我們先假設現在沒有鍵盤事件發生,因此,InputManager中的InputReader正在睡眠等待鍵盤事件的發生,而InputManager中的InputDispatcher正在等待InputReader從睡眠中醒過來並且喚醒它,而應用程序也正在消息循環中等待InputDispatcher從睡眠中醒過來並且喚醒它。這時候,用戶按下鍵盤中的一個鍵,於是,一系列喚醒的事件就依次發生了,一直到應用程序中正在顯示的Activity得到通知,有鍵盤事件發生了。我們先來看這個過程的序列圖,然後再詳細分析每一個步驟:


點擊查看大圖

        Step 1. InputReader.pollOnce

        Step 2. EventHub.getEvent

        這兩個函數分別定義在frameworks/base/libs/ui/InputReader.cpp和frameworks/base/libs/ui/EventHub.cpp文件中,前面我們在分析InputManager的啓動過程的Step 17和Step 18時,已經看到過這兩個函數了。InputReaderThread線程會不民地循環調用InputReader.pollOnce函數來讀入鍵盤事件,而實際的鍵盤事件讀入操作是由EventHub.getEvent函數來進行的。如果當前沒有鍵盤事件發生,InputReaderThread線程就會睡眠在EventHub.getEvent函數上,而當鍵盤事件發生後,就會把這個事件封裝成一個RawEvent對象,然後返回到pollOnce函數中,執行process函數進一步處理:

  1. void InputReader::loopOnce() {  
  2.     RawEvent rawEvent;  
  3.     mEventHub->getEvent(& rawEvent);  
  4.   
  5.     ......  
  6.   
  7.     process(& rawEvent);  
  8. }  
        Step 3. InputReader.process

        這個函數定義在frameworks/base/libs/ui/InputReader.cpp文件中:

  1. void InputReader::process(const RawEvent* rawEvent) {  
  2.     switch (rawEvent->type) {  
  3.     case EventHubInterface::DEVICE_ADDED:  
  4.         addDevice(rawEvent->deviceId);  
  5.         break;  
  6.   
  7.     case EventHubInterface::DEVICE_REMOVED:  
  8.         removeDevice(rawEvent->deviceId);  
  9.         break;  
  10.   
  11.     case EventHubInterface::FINISHED_DEVICE_SCAN:  
  12.         handleConfigurationChanged(rawEvent->when);  
  13.         break;  
  14.   
  15.     default:  
  16.         consumeEvent(rawEvent);  
  17.         break;  
  18.     }  
  19. }  

        當鍵盤事件發生時,rawEvent->type的值爲EV_KEY,這是一個宏定義,具體可以參考bionic/libc/kernel/common/linux/input.h文件:

  1. #define EV_KEY 0x01  
        因此,接下來會調用consumeEvent函數進一步處理。

        Step 4. InputReader.consumeEvent

        這個函數定義在frameworks/base/libs/ui/InputReader.cpp文件中:

  1. void InputReader::consumeEvent(const RawEvent* rawEvent) {  
  2.     int32_t deviceId = rawEvent->deviceId;  
  3.   
  4.     { // acquire device registry reader lock  
  5.         RWLock::AutoRLock _rl(mDeviceRegistryLock);  
  6.   
  7.         ssize_t deviceIndex = mDevices.indexOfKey(deviceId);  
  8.         if (deviceIndex < 0) {  
  9.             LOGW("Discarding event for unknown deviceId %d.", deviceId);  
  10.             return;  
  11.         }  
  12.   
  13.         InputDevice* device = mDevices.valueAt(deviceIndex);  
  14.         if (device->isIgnored()) {  
  15.             //LOGD("Discarding event for ignored deviceId %d.", deviceId);  
  16.             return;  
  17.         }  
  18.   
  19.         device->process(rawEvent);  
  20.     } // release device registry reader lock  
  21. }  
         首先從rawEvent中取得觸發鍵盤事件設備對象device,然後調用它的process函數進行處理。

         Step 5. InputDevice.process

         這個函數定義在frameworks/base/libs/ui/InputReader.cpp文件中:

  1. void InputDevice::process(const RawEvent* rawEvent) {  
  2.     size_t numMappers = mMappers.size();  
  3.     for (size_t i = 0; i < numMappers; i++) {  
  4.         InputMapper* mapper = mMappers[i];  
  5.         mapper->process(rawEvent);  
  6.     }  
  7. }  
         這裏的mMapper成員變量保存了一系列輸入設備事件處理象,例如負責處理鍵盤事件的KeyboardKeyMapper對象、負責處理軌跡球事件的TrackballInputMapper對象以及負責處理觸摸屏事件的TouchInputMapper對象, 它們是在InputReader類的成員函數createDevice中創建的。這裏查詢每一個InputMapper對象是否要對當前發生的事件進行處理。由於發生的是鍵盤事件,真正會對該事件進行處理的只有KeyboardKeyMapper對象。

         Step 6. KeyboardInputMapper.process

         這個函數定義在frameworks/base/libs/ui/InputReader.cpp文件中:

  1. void KeyboardInputMapper::process(const RawEvent* rawEvent) {  
  2.     switch (rawEvent->type) {  
  3.     case EV_KEY: {  
  4.         int32_t scanCode = rawEvent->scanCode;  
  5.         if (isKeyboardOrGamepadKey(scanCode)) {  
  6.             processKey(rawEvent->when, rawEvent->value != 0, rawEvent->keyCode, scanCode,  
  7.                     rawEvent->flags);  
  8.         }  
  9.         break;  
  10.     }  
  11.     }  
  12. }  
        這個函數首先會檢查一下鍵盤掃描碼是否正確,如果正確的話,就會調用processKey函數進一步處理。

        Step 7. KeyboardInputMapper.processKey

        這個函數定義在frameworks/base/libs/ui/InputReader.cpp文件中:

  1. void KeyboardInputMapper::processKey(nsecs_t when, bool down, int32_t keyCode,  
  2.         int32_t scanCode, uint32_t policyFlags) {  
  3.     int32_t newMetaState;  
  4.     nsecs_t downTime;  
  5.     bool metaStateChanged = false;  
  6.   
  7.     { // acquire lock  
  8.      AutoMutex _l(mLock);  
  9.   
  10.      if (down) {  
  11.          // Rotate key codes according to orientation if needed.  
  12.          // Note: getDisplayInfo is non-reentrant so we can continue holding the lock.  
  13.          if (mAssociatedDisplayId >= 0) {  
  14.              int32_t orientation;  
  15.              if (! getPolicy()->getDisplayInfo(mAssociatedDisplayId, NULL, NULL, & orientation)) {  
  16.                  return;  
  17.              }  
  18.   
  19.              keyCode = rotateKeyCode(keyCode, orientation);  
  20.          }  
  21.   
  22.          // Add key down.  
  23.          ssize_t keyDownIndex = findKeyDownLocked(scanCode);  
  24.          if (keyDownIndex >= 0) {  
  25.              // key repeat, be sure to use same keycode as before in case of rotation  
  26.              keyCode = mLocked.keyDowns.itemAt(keyDownIndex).keyCode;  
  27.          } else {  
  28.              // key down  
  29.              if ((policyFlags & POLICY_FLAG_VIRTUAL)  
  30.                  && mContext->shouldDropVirtualKey(when, getDevice(), keyCode, scanCode)) {  
  31.                      return;  
  32.              }  
  33.              mLocked.keyDowns.push();  
  34.              KeyDown& keyDown = mLocked.keyDowns.editTop();  
  35.              keyDown.keyCode = keyCode;  
  36.              keyDown.scanCode = scanCode;  
  37.          }  
  38.   
  39.          mLocked.downTime = when;  
  40.      } else {  
  41.          // Remove key down.  
  42.          ssize_t keyDownIndex = findKeyDownLocked(scanCode);  
  43.          if (keyDownIndex >= 0) {  
  44.              // key up, be sure to use same keycode as before in case of rotation  
  45.              keyCode = mLocked.keyDowns.itemAt(keyDownIndex).keyCode;  
  46.              mLocked.keyDowns.removeAt(size_t(keyDownIndex));  
  47.          } else {  
  48.              // key was not actually down  
  49.              LOGI("Dropping key up from device %s because the key was not down.  "  
  50.                  "keyCode=%d, scanCode=%d",  
  51.                  getDeviceName().string(), keyCode, scanCode);  
  52.              return;  
  53.          }  
  54.      }  
  55.   
  56.      int32_t oldMetaState = mLocked.metaState;  
  57.      newMetaState = updateMetaState(keyCode, down, oldMetaState);  
  58.      if (oldMetaState != newMetaState) {  
  59.          mLocked.metaState = newMetaState;  
  60.          metaStateChanged = true;  
  61.      }  
  62.   
  63.      downTime = mLocked.downTime;  
  64.     } // release lock  
  65.   
  66.   
  67.     if (metaStateChanged) {  
  68.         getContext()->updateGlobalMetaState();  
  69.     }  
  70.   
  71.     getDispatcher()->notifyKey(when, getDeviceId(), AINPUT_SOURCE_KEYBOARD, policyFlags,  
  72.         down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP,  
  73.         AKEY_EVENT_FLAG_FROM_SYSTEM, keyCode, scanCode, newMetaState, downTime);  
  74. }  
        這個函數首先對對按鍵作一些處理,例如,當某一個DPAD鍵被按下時,根據當時屏幕方向的不同,它所表示的意義也不同,因此,這裏需要根據當時屏幕的方向來調整鍵盤碼:
  1. // Rotate key codes according to orientation if needed.  
  2. // Note: getDisplayInfo is non-reentrant so we can continue holding the lock.  
  3. if (mAssociatedDisplayId >= 0) {  
  4.     int32_t orientation;  
  5.     if (! getPolicy()->getDisplayInfo(mAssociatedDisplayId, NULL, NULL, & orientation)) {  
  6.         return;  
  7.     }  
  8.   
  9.     keyCode = rotateKeyCode(keyCode, orientation);  
  10. }  
        如果這個鍵是一直按着不放的,不管屏幕的方向如何,必須保證後面的鍵盤碼和前面的一樣:
  1. // Add key down.  
  2. ssize_t keyDownIndex = findKeyDownLocked(scanCode);  
  3. if (keyDownIndex >= 0) {  
  4.     // key repeat, be sure to use same keycode as before in case of rotation  
  5.     keyCode = mLocked.keyDowns.itemAt(keyDownIndex).keyCode;  
  6. else {  
  7.     // key down  
  8.     if ((policyFlags & POLICY_FLAG_VIRTUAL)  
  9.         && mContext->shouldDropVirtualKey(when, getDevice(), keyCode, scanCode)) {  
  10.          return;  
  11.     }  
  12.     mLocked.keyDowns.push();  
  13.     KeyDown& keyDown = mLocked.keyDowns.editTop();  
  14.     keyDown.keyCode = keyCode;  
  15.     keyDown.scanCode = scanCode;  
  16. }  
       如果是第一次按下某個鍵,還必須把它保存在mLocked.keyDowns裏面,就是爲了處理上面講的當這個鍵盤一直按着不放的時候屏幕方向發生改變的情況。
       如果是鬆開鍵盤上的某個鍵,就把它從mLocked.keyDowns裏面刪除:
  1. // Remove key down.  
  2. ssize_t keyDownIndex = findKeyDownLocked(scanCode);  
  3. if (keyDownIndex >= 0) {  
  4.     // key up, be sure to use same keycode as before in case of rotation  
  5.     keyCode = mLocked.keyDowns.itemAt(keyDownIndex).keyCode;  
  6.     mLocked.keyDowns.removeAt(size_t(keyDownIndex));  
  7. else {  
  8.     // key was not actually down  
  9.     LOGI("Dropping key up from device %s because the key was not down.  "  
  10.         "keyCode=%d, scanCode=%d",  
  11.         getDeviceName().string(), keyCode, scanCode);  
  12.     return;  
  13. }  
        當然,對鍵盤事件的這些處理不是本文的重點,本文的重點是分析從鍵盤事件到當前激活的Activity窗口接收到這個鍵盤消息的過程。

        最後,KeyboardInputMappger函數通知InputDispatcher,有鍵盤事件發生了:

  1. getDispatcher()->notifyKey(when, getDeviceId(), AINPUT_SOURCE_KEYBOARD, policyFlags,  
  2.     down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP,  
  3.     AKEY_EVENT_FLAG_FROM_SYSTEM, keyCode, scanCode, newMetaState, downTime);  
        Step 8. InputDispatcher.notifyKey

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

  1. void InputDispatcher::notifyKey(nsecs_t eventTime, int32_t deviceId, int32_t source,  
  2.     uint32_t policyFlags, int32_t action, int32_t flags,  
  3.     int32_t keyCode, int32_t scanCode, int32_t metaState, nsecs_t downTime) {  
  4.     ......  
  5.   
  6.     if (! validateKeyEvent(action)) {  
  7.         return;  
  8.     }  
  9.   
  10.     /* According to http://source.android.com/porting/keymaps_keyboard_input.html 
  11.     * Key definitions: Key definitions follow the syntax key SCANCODE KEYCODE [FLAGS...], 
  12.     * where SCANCODE is a number, KEYCODE is defined in your specific keylayout file 
  13.     * (android.keylayout.xxx), and potential FLAGS are defined as follows: 
  14.     *     SHIFT: While pressed, the shift key modifier is set 
  15.     *     ALT: While pressed, the alt key modifier is set 
  16.     *     CAPS: While pressed, the caps lock key modifier is set 
  17.     *     Since KeyEvent.java doesn't check if Cap lock is ON and we don't have a 
  18.     *     modifer state for cap lock, we will not support it. 
  19.     */  
  20.     if (policyFlags & POLICY_FLAG_ALT) {  
  21.         metaState |= AMETA_ALT_ON | AMETA_ALT_LEFT_ON;  
  22.     }  
  23.     if (policyFlags & POLICY_FLAG_ALT_GR) {  
  24.         metaState |= AMETA_ALT_ON | AMETA_ALT_RIGHT_ON;  
  25.     }  
  26.     if (policyFlags & POLICY_FLAG_SHIFT) {  
  27.         metaState |= AMETA_SHIFT_ON | AMETA_SHIFT_LEFT_ON;  
  28.     }  
  29.   
  30.     policyFlags |= POLICY_FLAG_TRUSTED;  
  31.     mPolicy->interceptKeyBeforeQueueing(eventTime, deviceId, action, /*byref*/ flags,  
  32.         keyCode, scanCode, /*byref*/ policyFlags);  
  33.   
  34.     bool needWake;  
  35.     { // acquire lock  
  36.         AutoMutex _l(mLock);  
  37.   
  38.         int32_t repeatCount = 0;  
  39.         KeyEntry* newEntry = mAllocator.obtainKeyEntry(eventTime,  
  40.             deviceId, source, policyFlags, action, flags, keyCode, scanCode,  
  41.             metaState, repeatCount, downTime);  
  42.   
  43.         needWake = enqueueInboundEventLocked(newEntry);  
  44.     } // release lock  
  45.   
  46.     if (needWake) {  
  47.         mLooper->wake();  
  48.     }  
  49. }  
        函數首先是調用validateKeyEvent函數來驗證action參數是否正確:
  1. static bool isValidKeyAction(int32_t action) {  
  2.     switch (action) {  
  3.     case AKEY_EVENT_ACTION_DOWN:  
  4.     case AKEY_EVENT_ACTION_UP:  
  5.         return true;  
  6.     default:  
  7.         return false;  
  8.     }  
  9. }  
  10.   
  11. static bool validateKeyEvent(int32_t action) {  
  12.     if (! isValidKeyAction(action)) {  
  13.         LOGE("Key event has invalid action code 0x%x", action);  
  14.         return false;  
  15.     }  
  16.     return true;  
  17. }  
        正確的action參數的值只能爲AKEY_EVENT_ACTION_DOWN(按下)或者AKEY_EVENT_ACTION_UP(鬆開)。

        參數action檢查通過後,還通過policyFlags參數來檢查一下同時是否有ALT和SHIFT鍵被按下:

  1. if (policyFlags & POLICY_FLAG_ALT) {  
  2.     metaState |= AMETA_ALT_ON | AMETA_ALT_LEFT_ON;  
  3. }  
  4. if (policyFlags & POLICY_FLAG_ALT_GR) {  
  5.     metaState |= AMETA_ALT_ON | AMETA_ALT_RIGHT_ON;  
  6. }  
  7. if (policyFlags & POLICY_FLAG_SHIFT) {  
  8.     metaState |= AMETA_SHIFT_ON | AMETA_SHIFT_LEFT_ON;  
  9. }  
        最後,調用enqueueInboundEventLocked函數把這個按鍵事件封裝成一個KeyEntry結構加入到InputDispatcher類的mInboundQueue隊列中去:
  1. bool InputDispatcher::enqueueInboundEventLocked(EventEntry* entry) {  
  2.     bool needWake = mInboundQueue.isEmpty();  
  3.     mInboundQueue.enqueueAtTail(entry);  
  4.   
  5.     switch (entry->type) {  
  6.     case EventEntry::TYPE_KEY: {  
  7.         KeyEntry* keyEntry = static_cast<KeyEntry*>(entry);  
  8.         if (isAppSwitchKeyEventLocked(keyEntry)) {  
  9.             if (keyEntry->action == AKEY_EVENT_ACTION_DOWN) {  
  10.                 mAppSwitchSawKeyDown = true;  
  11.             } else if (keyEntry->action == AKEY_EVENT_ACTION_UP) {  
  12.                 if (mAppSwitchSawKeyDown) {  
  13. <span style="white-space:pre">        </span>    ......  
  14.                     mAppSwitchDueTime = keyEntry->eventTime + APP_SWITCH_TIMEOUT;  
  15.                     mAppSwitchSawKeyDown = false;  
  16.                     needWake = true;  
  17.                 }  
  18.             }  
  19.         }  
  20.         break;  
  21.     }  
  22.     }  
  23.   
  24.     return needWake;  
  25. }  
        從這個函數我們可以看出,在兩種情況下,它的返回值爲true,一是當加入該鍵盤事件到mInboundQueue之前,mInboundQueue爲空,這表示InputDispatccherThread線程正在睡眠等待InputReaderThread線程的喚醒,因此,它返回true表示要喚醒InputDispatccherThread線程;二是加入該鍵盤事件到mInboundQueue之前,mInboundQueue不爲空,但是此時用戶按下的是Home鍵,按下Home鍵表示要切換App,我們知道,在切換App時,新的App會把它的鍵盤消息接收通道註冊到InputDispatcher中去,並且會等待InputReader的喚醒,因此,在這種情況下,也需要返回true,表示要喚醒InputDispatccherThread線程。如果不是這兩種情況,那麼就說明InputDispatccherThread線程現在正在處理前面的鍵盤事件,不需要喚醒它。

        回到前面的notifyKey函數中,根據enqueueInboundEventLocked函數的返回值來決定是否要喚醒InputDispatccherThread線程:

  1. if (needWake) {  
  2.     mLooper->wake();  
  3. }  
        這裏,假設needWake爲true,於是,就會調用mLooper對象的wake函數來喚醒InputDispatccherThread線程了。

        Step 9. Looper.wake

        這個函數定義在frameworks/base/libs/utils/Looper.cpp文件中,在前面一篇文章Android應用程序消息處理機制(Looper、Handler)分析中,我們已經分析過這個函數了,這裏不再詳述,簡單來說,它的作用就是用來喚醒睡眠在Looper對象內部的管道讀端的線程,在我們的這個場景中,睡眠在Looper對象內部的管道讀端的線程就是InputDispatccherThread線程了。

        從上面InputManager啓動過程的Step 15中,我們知道,此時InputDispatccherThread線程正在InputDispatcher類的dispatchOnceb函數中通過調用mLooper->loopOnce函數進入睡眠狀態。當它被喚醒以後,它就會從InputDispatcher類的dispatchOnceb函數返回到InputDispatcherThread類的threadLoop函數,而InputDispatcherThread類的threadLoop函數是循環執行的,於是,它又會再次進入到InputDispatcher類的dispatchOnce函數來處理當前發生的鍵盤事件。

        Step 10. InputDispatcher.dispatchOnce

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

  1. void InputDispatcher::dispatchOnce() {  
  2.     nsecs_t keyRepeatTimeout = mPolicy->getKeyRepeatTimeout();  
  3.     nsecs_t keyRepeatDelay = mPolicy->getKeyRepeatDelay();  
  4.   
  5.     nsecs_t nextWakeupTime = LONG_LONG_MAX;  
  6.     { // acquire lock  
  7.         AutoMutex _l(mLock);  
  8.         dispatchOnceInnerLocked(keyRepeatTimeout, keyRepeatDelay, & nextWakeupTime);  
  9.   
  10.         ......  
  11.     } // release lock  
  12.   
  13.     ......  
  14. }  
        它調用dispatchOnceInnerLocked函數來進一步處理這個鍵盤事件。

        Step 11. InputDispatcher.dispatchOnceInnerLocked

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

  1. void InputDispatcher::dispatchOnceInnerLocked(nsecs_t keyRepeatTimeout,  
  2.     nsecs_t keyRepeatDelay, nsecs_t* nextWakeupTime) {  
  3.     ......  
  4.   
  5.     // Ready to start a new event.  
  6.     // If we don't already have a pending event, go grab one.  
  7.     if (! mPendingEvent) {  
  8.         if (mInboundQueue.isEmpty()) {  
  9.             ......  
  10.         } else {  
  11.             // Inbound queue has at least one entry.  
  12.             EventEntry* entry = mInboundQueue.headSentinel.next;  
  13.   
  14.             ......  
  15.   
  16.             mInboundQueue.dequeue(entry);  
  17.             mPendingEvent = entry;  
  18.         }  
  19.   
  20.         ......  
  21.     }  
  22.   
  23.     ......  
  24.   
  25.     switch (mPendingEvent->type) {  
  26.     ......  
  27.   
  28.     case EventEntry::TYPE_KEY: {  
  29.         KeyEntry* typedEntry = static_cast<KeyEntry*>(mPendingEvent);  
  30.         ......  
  31.         done = dispatchKeyLocked(currentTime, typedEntry, keyRepeatTimeout,  
  32.             &dropReason, nextWakeupTime);  
  33.         break;  
  34.                                }  
  35.   
  36.     ......  
  37.     }  
  38.   
  39.     ......  
  40. }  

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

  1. mInboundQueue.dequeue(entry);  
  2. mPendingEvent = entry;  
        由於這裏發生的是鍵盤事件,即mPendingEvent->type的值爲EventEntry::TYPE_KEY,於是,在接下來的switch語句中就會執行dispatchKeyLocked函數來分發鍵盤消息。

        Step 12. InputDispatcher.dispatchKeyLocked

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

  1. bool InputDispatcher::dispatchKeyLocked(  
  2.         nsecs_t currentTime, KeyEntry* entry, nsecs_t keyRepeatTimeout,  
  3.         DropReason* dropReason, nsecs_t* nextWakeupTime) {  
  4.     ......  
  5.   
  6.     // Identify targets.  
  7.     if (! mCurrentInputTargetsValid) {  
  8.         int32_t injectionResult = findFocusedWindowTargetsLocked(currentTime,  
  9.             entry, nextWakeupTime);  
  10.   
  11.         ......  
  12.     }  
  13.   
  14.     // Dispatch the key.  
  15.     dispatchEventToCurrentInputTargetsLocked(currentTime, entry, false);  
  16.     return true;  
  17. }  
         InputDispatcher類中的mCurrentInputTargetsValid成員變量表示InputDispatcher是否已經標誌出誰是當前激活的Activity窗口,如果沒有,就需要通過findFocusedWindowTargetsLocked函數來把它找出來。當把當前激活的Activity窗口找出來以後,接下來就調用dispatchEventToCurrentInputTargetsLocked函數把鍵盤事件分發給它了。

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

        Step 13. InputDispatcher.findFocusedWindowTargetsLocked

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

  1. int32_t InputDispatcher::findFocusedWindowTargetsLocked(nsecs_t currentTime,  
  2.         const EventEntry* entry, nsecs_t* nextWakeupTime) {  
  3.     mCurrentInputTargets.clear();  
  4.   
  5.     int32_t injectionResult;  
  6.   
  7.     // If there is no currently focused window and no focused application  
  8.     // then drop the event.  
  9.     if (! mFocusedWindow) {  
  10.         if (mFocusedApplication) {  
  11.             ......  
  12.             injectionResult = handleTargetsNotReadyLocked(currentTime, entry,  
  13.                 mFocusedApplication, NULL, nextWakeupTime);  
  14.             goto Unresponsive;  
  15.         }  
  16.   
  17.         ......  
  18.         injectionResult = INPUT_EVENT_INJECTION_FAILED;  
  19.         goto Failed;  
  20.     }  
  21.   
  22.     // Check permissions.  
  23.     if (! checkInjectionPermission(mFocusedWindow, entry->injectionState)) {  
  24.         injectionResult = INPUT_EVENT_INJECTION_PERMISSION_DENIED;  
  25.         goto Failed;  
  26.     }  
  27.   
  28.     // If the currently focused window is paused then keep waiting.  
  29.     if (mFocusedWindow->paused) {  
  30.         ......  
  31.         injectionResult = handleTargetsNotReadyLocked(currentTime, entry,  
  32.             mFocusedApplication, mFocusedWindow, nextWakeupTime);  
  33.         goto Unresponsive;  
  34.     }  
  35.   
  36.     // If the currently focused window is still working on previous events then keep waiting.  
  37.     if (! isWindowFinishedWithPreviousInputLocked(mFocusedWindow)) {  
  38.         ......  
  39.         injectionResult = handleTargetsNotReadyLocked(currentTime, entry,  
  40.             mFocusedApplication, mFocusedWindow, nextWakeupTime);  
  41.         goto Unresponsive;  
  42.     }  
  43.   
  44.     // Success!  Output targets.  
  45.     injectionResult = INPUT_EVENT_INJECTION_SUCCEEDED;  
  46.     addWindowTargetLocked(mFocusedWindow, InputTarget::FLAG_FOREGROUND, BitSet32(0));  
  47.   
  48.     ......  
  49.   
  50.     return injectionResult;  
  51. }  
        回憶前面我們分析應用程序註冊鍵盤消息接收通道的過程時,在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

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

  1. void InputDispatcher::addWindowTargetLocked(const InputWindow* window, int32_t targetFlags,  
  2.         BitSet32 pointerIds) {  
  3.     mCurrentInputTargets.push();  
  4.   
  5.     InputTarget& target = mCurrentInputTargets.editTop();  
  6.     target.inputChannel = window->inputChannel;  
  7.     target.flags = targetFlags;  
  8.     target.xOffset = - window->frameLeft;  
  9.     target.yOffset = - window->frameTop;  
  10.     target.pointerIds = pointerIds;  
  11. }  
        這個函數簡單,就是把傳進來的參數window添加到mCurrentInputTargets中去就完事了,後面InputDispatcher就會從mCurrentInputTargets中取出恰當的Activity窗口,然後把鍵盤事件分發給它。

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

        Step 15. InputDispatcher.dispatchEventToCurrentInputTargetsLocked

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

  1. void InputDispatcher::dispatchEventToCurrentInputTargetsLocked(nsecs_t currentTime,  
  2.         EventEntry* eventEntry, bool resumeWithAppendedMotionSample) {  
  3.    ......  
  4.   
  5.    for (size_t i = 0; i < mCurrentInputTargets.size(); i++) {  
  6.        const InputTarget& inputTarget = mCurrentInputTargets.itemAt(i);  
  7.   
  8.        ssize_t connectionIndex = getConnectionIndexLocked(inputTarget.inputChannel);  
  9.        if (connectionIndex >= 0) {  
  10.            sp<Connection> connection = mConnectionsByReceiveFd.valueAt(connectionIndex);  
  11.            prepareDispatchCycleLocked(currentTime, connection, eventEntry, & inputTarget,  
  12.                resumeWithAppendedMotionSample);  
  13.        } else {  
  14.            ......  
  15.    }  
  16. }  
        這個函數的實現也比較簡單,前面我們已經把當前需要接受鍵盤事件的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函數來進一步處理。

        Step 16. InputDispatcher.prepareDispatchCycleLocked

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

  1. void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime,  
  2.         const sp<Connection>& connection, EventEntry* eventEntry, const InputTarget* inputTarget,  
  3.         bool resumeWithAppendedMotionSample) {  
  4.      ......  
  5.   
  6.   
  7.      // Resume the dispatch cycle with a freshly appended motion sample.  
  8.      // First we check that the last dispatch entry in the outbound queue is for the same  
  9.      // motion event to which we appended the motion sample.  If we find such a dispatch  
  10.      // entry, and if it is currently in progress then we try to stream the new sample.  
  11.      bool wasEmpty = connection->outboundQueue.isEmpty();  
  12.   
  13.      if (! wasEmpty && resumeWithAppendedMotionSample) {  
  14.          ......  
  15.          return;  
  16.      }  
  17.   
  18.      // This is a new event.  
  19.      // Enqueue a new dispatch entry onto the outbound queue for this connection.  
  20.      DispatchEntry* dispatchEntry = mAllocator.obtainDispatchEntry(eventEntry, // increments ref  
  21.          inputTarget->flags, inputTarget->xOffset, inputTarget->yOffset);  
  22.   
  23.      ......  
  24.   
  25.      // Enqueue the dispatch entry.  
  26.      connection->outboundQueue.enqueueAtTail(dispatchEntry);  
  27.   
  28.      // If the outbound queue was previously empty, start the dispatch cycle going.  
  29.      if (wasEmpty) {  
  30.          ......  
  31.   
  32.          startDispatchCycleLocked(currentTime, connection);  
  33.      }  
  34. }  

         在開始處理鍵盤事件之前,這個函數會檢查一下傳進來的參數connection中的outboundQueue事件隊列是否爲空,如果不爲空,就要看看當前要處理的事件和outboundQueue隊列中的最後一個事件是不是同一個motion事件,如果是的話,並且從上面傳進來的resumeWithAppendedMotionSample參數爲true,這時候就要以流水線的方式來處理這些motion事件了。在我們這個情景中,要處理的是鍵盤事件,因此在上面Step 12中傳進來的resumeWithAppendedMotionSample參數爲false,因此,我們略過這種情況。

         接下來,就會把當前的鍵盤事件封裝成一個DispatchEntry對象,然後添加到connection對象的outboundQueue隊列中去,表示當前鍵盤事件是一個待處理的鍵盤事件。    

         當connection中的outboundQueue事件隊列不爲空,即wasEmpty爲false時,說明當前這個Activity窗口正在處鍵盤事件了,因此,就不需要調用startDispatchCycleLocked來啓動Activity窗口來處理這個事件了,因爲一旦這個Activity窗口正在處鍵盤事件,它就會一直處理下去,直到它裏的connection對象的outboundQueue爲空爲止。當connection中的outboundQueue事件隊列爲空時,就需要調用startDispatchCycleLocked來通知這個Activity窗口來執行鍵盤事件處理的流程了。

         Step 17. InputDispatcher.startDispatchCycleLocked

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

  1. void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime,  
  2.     const sp<Connection>& connection) {  
  3.     ......  
  4.   
  5.     DispatchEntry* dispatchEntry = connection->outboundQueue.headSentinel.next;  
  6.   
  7.     // Mark the dispatch entry as in progress.  
  8.     dispatchEntry->inProgress = true;  
  9.   
  10.     // Update the connection's input state.  
  11.     EventEntry* eventEntry = dispatchEntry->eventEntry;  
  12.     ......  
  13.   
  14.     // Publish the event.  
  15.     status_t status;  
  16.     switch (eventEntry->type) {  
  17.     case EventEntry::TYPE_KEY: {  
  18.         KeyEntry* keyEntry = static_cast<KeyEntry*>(eventEntry);  
  19.   
  20.         // Apply target flags.  
  21.         int32_t action = keyEntry->action;  
  22.         int32_t flags = keyEntry->flags;  
  23.   
  24.         // Publish the key event.  
  25.         status = connection->inputPublisher.publishKeyEvent(keyEntry->deviceId, keyEntry->source,  
  26.             action, flags, keyEntry->keyCode, keyEntry->scanCode,  
  27.             keyEntry->metaState, keyEntry->repeatCount, keyEntry->downTime,  
  28.             keyEntry->eventTime);  
  29.   
  30.         ......  
  31.         break;  
  32.     }  
  33.     ......  
  34.     }  
  35.   
  36.     // Send the dispatch signal.  
  37.     status = connection->inputPublisher.sendDispatchSignal();  
  38.       
  39.     ......  
  40. }  
         這個函數主要圍繞傳進來的Connection對象做兩件事情,一是從它的outboundQueue隊列中取出當前需要處理的鍵盤事件,然後把這個事件記錄在它的內部對象inputPublisher中,二是通過它的內部對象inputPublisher通知它所關聯的Activity窗口,現在有鍵盤事件需要處理了。第一件事情是通過調用它的InputPublisher對象的publishKeyEvent函數來完成的,而第二件事情是通過調用它的InputPublisher對象的sendDispatchSignal來完成的。我們先來看InputPublisher的成員函數publishKeyEvent的實現,然後再回來分析它的另外一個成員函數sendDispatchSignal的實現。

        Step 18. InputPublisher.publishKeyEvent

        這個函數定義在frameworks/base/libs/ui/InputTransport.cpp文件中:

  1. status_t InputPublisher::publishKeyEvent(  
  2.     int32_t deviceId,  
  3.     int32_t source,  
  4.     int32_t action,  
  5.     int32_t flags,  
  6.     int32_t keyCode,  
  7.     int32_t scanCode,  
  8.     int32_t metaState,  
  9.     int32_t repeatCount,  
  10.     nsecs_t downTime,  
  11.     nsecs_t eventTime) {  
  12.     ......  
  13.   
  14.     status_t result = publishInputEvent(AINPUT_EVENT_TYPE_KEY, deviceId, source);  
  15.     if (result < 0) {  
  16.         return result;  
  17.     }  
  18.   
  19.     mSharedMessage->key.action = action;  
  20.     mSharedMessage->key.flags = flags;  
  21.     mSharedMessage->key.keyCode = keyCode;  
  22.     mSharedMessage->key.scanCode = scanCode;  
  23.     mSharedMessage->key.metaState = metaState;  
  24.     mSharedMessage->key.repeatCount = repeatCount;  
  25.     mSharedMessage->key.downTime = downTime;  
  26.     mSharedMessage->key.eventTime = eventTime;  
  27.     return OK;  
  28. }  
        這個函數主要就是把鍵盤事件記錄在InputPublisher類的成員變量mSharedMessage中了,這個mSharedMessage成員變量指向的是一個匿名共享內存。

        這個匿名共享內存是什麼時候創建的呢?前面我們在分析應用程序註冊鍵盤消息接收通道的過程時,在Step 18中(InputDispatcher.registerInputChannel),在把Server端的InputChannel封裝成一個 Connection對象時,會調用它的initialize成員函數來執行一些初始化工作,就是在這個時候創建這個匿名共享內存的了:

  1. sp<Connection> connection = new Connection(inputChannel);  
  2. status_t status = connection->initialize();  
       我們來看一下這個initialize函數的實現,它定義在frameworks/base/libs/ui/InputTransport.cpp文件中:
  1. status_t InputPublisher::initialize() {  
  2.     ......  
  3.   
  4.     int ashmemFd = mChannel->getAshmemFd();  
  5.     int result = ashmem_get_size_region(ashmemFd);  
  6.     ......  
  7.   
  8.     mAshmemSize = (size_t) result;  
  9.   
  10.     mSharedMessage = static_cast<InputMessage*>(mmap(NULL, mAshmemSize,  
  11.         PROT_READ | PROT_WRITE, MAP_SHARED, ashmemFd, 0));  
  12.     ......  
  13.   
  14.     mPinned = true;  
  15.     mSharedMessage->consumed = false;  
  16.   
  17.     return reset();  
  18. }  
        InputPublisher的成員變量mChannel就是指註冊在InputDispatcher中的Server端InputChannel了。我們知道,這個InputChannel除了擁有一個反向管道的讀端文件描述符和一個前向管道的寫端文件描述符之後,還有一個匿名共享文件描述符,這個匿名共享文件描述符就是用來創建匿名共享內存mSharedMessage的了。

        這個匿名共享內存mSharedMessage的作用是什麼呢?原來,在InputChannel中,前向管道和反向管道的作用只是用來在Server端和Client端之間相互通知有事件發生了,但是具體是什麼樣的事件,還需要去讀取這個匿名共享內存的內容才知道。前面我們在分析應用程序註冊鍵盤消息接收通道的過程時,在Step 14中(InputChannel.openInputChannelPair)創建Server端和Client端的InputChannel對時,創建一個匿名共享內存,這個匿名共享內存有兩個文件描述符同時指向它,其中一個放在Server端的InputChannel中,另外一個放在Client端的InputChannel中。這樣,當InputDispatcher通過Server端的InputChannel的前向管道來通知Client端有鍵盤事件發生時,Client端只要通過它的InputChannel中的匿名共享內存文件描述符去讀取匿名共享內存中的內容,就可以知道發生了什麼事情了。有關匿名共享內存的相關知識,請參考Android系統匿名共享內存Ashmem(Anonymous Shared Memory)簡要介紹和學習計劃一文。

        回到Step 17中,接下來就是調用InputPublisher的成員函數sendDispatchSignal來通知Activity窗口處理鍵盤事件了。

        Step 19. InputPublishe.sendDispatchSignal
        這個函數定義在frameworks/base/libs/ui/InputTransport.cpp文件中:

  1. status_t InputPublisher::sendDispatchSignal() {  
  2.     ......  
  3.   
  4.     mWasDispatched = true;  
  5.     return mChannel->sendSignal(INPUT_SIGNAL_DISPATCH);  
  6. }  
        這個函數很簡單,它通過調用內部成員變量mChannel的sendSignal函數來通知相應的Activity窗口來處理鍵盤事件。

        Step 20. InputChannel.sendSignal

        這個函數定義在frameworks/base/libs/ui/InputTransport.cpp文件中:

  1. status_t InputChannel::sendSignal(char signal) {  
  2.     ssize_t nWrite;  
  3.     do {  
  4.         nWrite = ::write(mSendPipeFd, & signal, 1);  
  5.     } while (nWrite == -1 && errno == EINTR);  
  6.   
  7.     if (nWrite == 1) {  
  8.         ......  
  9.         return OK;  
  10.     }  
  11.   
  12.     return -errno;  
  13. }  
        這裏所謂的發送信號通知,其實是通過向其內部一個管道的寫端寫入一個字符來實現的。前面我們分析應用程序註冊鍵盤消息接收通道的過程時,在Step 21中(NativeInputQueue.registerInputChannel),它把一個InputChannel註冊到應用程序主線程中的Looper對象中,然後應用程序的主線程就通過這個Looper對象睡眠等待在這個InputChannel中的前向管道中有新的內容可讀了,這裏的mSendPipeFd就是對應這個前向管道的寫端。現在既然向這個前向管道的寫端寫入新的內容了,於是,應用程序的主線程就被喚醒了。

        在前面分析應用程序註冊鍵盤消息接收通道過程的Step 21中,我們也說過,當應用程序的主線程因爲這個InputChannel中的前向管道的寫端喚醒時,NativeInputQueue的成員函數handleReceiveCallback就會被回調,因此,接下來,應用程序的主線程就會被喚醒,然後執行NativeInputQueue的成員函數handleReceiveCallback。

        Step 21. NativeInputQueue.handleReceiveCallback

        這個函數定義在frameworks/base/core/jni/android_view_InputQueue.cpp文件中:

  1. int NativeInputQueue::handleReceiveCallback(int receiveFd, int events, void* data) {  
  2.     NativeInputQueue* q = static_cast<NativeInputQueue*>(data);  
  3.     JNIEnv* env = AndroidRuntime::getJNIEnv();  
  4.   
  5.     sp<Connection> connection;  
  6.     InputEvent* inputEvent;  
  7.     jobject inputHandlerObjLocal;  
  8.     jlong finishedToken;  
  9.     { // acquire lock  
  10.         AutoMutex _l(q->mLock);  
  11.   
  12.         ssize_t connectionIndex = q->mConnectionsByReceiveFd.indexOfKey(receiveFd);  
  13.         ......  
  14.   
  15.             connection = q->mConnectionsByReceiveFd.valueAt(connectionIndex);  
  16.         ......  
  17.   
  18.   
  19.             status_t status = connection->inputConsumer.receiveDispatchSignal();  
  20.         if (status) {  
  21.             ......  
  22.                 return 0; // remove the callback  
  23.         }  
  24.   
  25.         ......  
  26.   
  27.         status = connection->inputConsumer.consume(& connection->inputEventFactory, & inputEvent);  
  28.         ......  
  29.   
  30.         finishedToken = generateFinishedToken(receiveFd, connection->id, connection->messageSeqNum);  
  31.   
  32.         inputHandlerObjLocal = env->NewLocalRef(connection->inputHandlerObjGlobal);  
  33.     } // release lock  
  34.   
  35.     ......  
  36.   
  37.     int32_t inputEventType = inputEvent->getType();  
  38.   
  39.     jobject inputEventObj;  
  40.     jmethodID dispatchMethodId;  
  41.     switch (inputEventType) {  
  42.     case AINPUT_EVENT_TYPE_KEY:  
  43.         ......  
  44.             inputEventObj = android_view_KeyEvent_fromNative(env,  
  45.             static_cast<KeyEvent*>(inputEvent));  
  46.         dispatchMethodId = gInputQueueClassInfo.dispatchKeyEvent;  
  47.         break;  
  48.     }  
  49.     ......  
  50.     }  
  51.   
  52.     ......  
  53.   
  54.     env->CallStaticVoidMethod(gInputQueueClassInfo.clazz,  
  55.                           dispatchMethodId, inputHandlerObjLocal, inputEventObj,  
  56.                           jlong(finishedToken));  
  57.   
  58.     ......  
  59.   
  60.     return 1;  
  61. }  
        這個函數首先是通過參數data獲得當初註冊InputChannel的NativeInputQueue對象,具體可以參考前面介紹的應用程序註冊鍵盤消息接收通道過程的Step 21。接下來再通過參數receiveFd獲得保存在這個NativeInputQueue對象中的mConnectionsByReceiveFd成員變量中的Connection對象。有了這個Connection對象後,就可以獲得它內部的InputConsumer對象,這個InputConsumer對象是和上面的Step 18中介紹的InputPublisher對象相應的。

        在InputChannel內部中,分別有一個InputPublisher對象和一個InputConsumer對象,對於Server端的InputChannel來說,它使用的是InputPublisher對象,通過它進行鍵盤消息的分發,而對於Client端的InputChannel來說,它使用的是InputConsumer對象,通過它進行鍵盤消息的讀取。

        獲得了這個InputConsumer對象後首先是調用它的receiveDispatchSignal來確認是否是接收到了鍵盤消息的通知,如果是的話,再調用它的consume函數來把鍵盤事件讀取出來,最後,調用Java層的回調對象InputQueue的DispatchKeyEvent來處理這個鍵盤事件。下面,我們就依次來分析這些過程。

        Step 22. InputConsumer.receiveDispatchSignal

        這個函數定義在frameworks/base/libs/ui/InputTransport.cpp文件中:

  1. status_t InputConsumer::receiveDispatchSignal() {  
  2.     ......  
  3.   
  4.     char signal;  
  5.     status_t result = mChannel->receiveSignal(& signal);  
  6.     if (result) {  
  7.         return result;  
  8.     }  
  9.     if (signal != INPUT_SIGNAL_DISPATCH) {  
  10.         ......  
  11.         return UNKNOWN_ERROR;  
  12.     }  
  13.     return OK;  
  14. }  
         這個函數很簡單,它通過它內部對象mChannel來從前向管道的讀端讀入一個字符,看看是否是前面的Step 20中寫入的INPUT_SIGNAL_DISPATCH字符。

         InputChannel類的receiveSignal函數也是定義在frameworks/base/libs/ui/InputTransport.cpp文件中:

  1. status_t InputChannel::receiveSignal(char* outSignal) {  
  2.     ssize_t nRead;  
  3.     do {  
  4.         nRead = ::read(mReceivePipeFd, outSignal, 1);  
  5.     } while (nRead == -1 && errno == EINTR);  
  6.   
  7.     if (nRead == 1) {  
  8.         ......  
  9.         return OK;  
  10.     }  
  11.   
  12.     ......  
  13.     return -errno;  
  14. }  
        Step 23. InputConsumer.consume

        這個函數定義在frameworks/base/libs/ui/InputTransport.cpp文件中:

  1. status_t InputConsumer::consume(InputEventFactoryInterface* factory, InputEvent** outEvent) {  
  2.     ......  
  3.   
  4.     *outEvent = NULL;  
  5.   
  6.     int ashmemFd = mChannel->getAshmemFd();  
  7.     int result = ashmem_pin_region(ashmemFd, 0, 0);  
  8.     ......  
  9.   
  10.     if (mSharedMessage->consumed) {  
  11.         ......  
  12.         return INVALID_OPERATION;  
  13.     }  
  14.   
  15.     // Acquire but *never release* the semaphore.  Contention on the semaphore is used to signal  
  16.     // to the publisher that the message has been consumed (or is in the process of being  
  17.     // consumed).  Eventually the publisher will reinitialize the semaphore for the next message.  
  18.     result = sem_wait(& mSharedMessage->semaphore);  
  19.     ......  
  20.   
  21.     mSharedMessage->consumed = true;  
  22.   
  23.     switch (mSharedMessage->type) {  
  24.     case AINPUT_EVENT_TYPE_KEY: {  
  25.         KeyEvent* keyEvent = factory->createKeyEvent();  
  26.         if (! keyEvent) return NO_MEMORY;  
  27.   
  28.         populateKeyEvent(keyEvent);  
  29.   
  30.         *outEvent = keyEvent;  
  31.         break;  
  32.     }  
  33.     ......  
  34.     }  
  35.   
  36.     return OK;  
  37. }  
        這個函數很簡單,只要對照前面的Step 18(InputPublisher.publishKeyEvent)來邏輯來看就可以了,後者是往匿名共享內存中寫入鍵盤事件,前者是從這個匿名共享內存中把這個鍵盤事件的內容讀取出來。

        回到Step 21中的handleReceiveCallback函數中,從InputConsumer中獲得了鍵盤事件的內容(保存在本地變量inputEvent中)後,就開始要通知Java層的應用程序了。在前面分析應用程序註冊鍵盤消息接收通道的過程時,在Step 21中(NativeInputQueue.registerInputChannel),會把傳進來的對象inputHandlerObj保存在Connection對象中:

  1. connection->inputHandlerObjGlobal = env->NewGlobalRef(inputHandlerObj);  
        這個inputHandlerObj對象的類型爲Java層的InputHandler對象,因此,這裏首先把它取回來:
  1. inputHandlerObjLocal = env->NewLocalRef(connection->inputHandlerObjGlobal);  
        取回來之後,我們要把作爲參數來調用InputQueue類的dispatchKeyEvent靜態成員函數來通知應用程序,有鍵盤事件發生了,因此,先找到InputQueue類的靜態成員函數dispatchKeyEvent的ID:
  1. dispatchMethodId = gInputQueueClassInfo.dispatchKeyEvent;  
        在回調用這個InputQueue類的dispatchKeyEvent靜態成員函數之前,還要把前面獲得的inputEvent對象轉換成Java層的KeyEvent對象:
  1. inputEventObj = android_view_KeyEvent_fromNative(env,  
  2.     static_cast<KeyEvent*>(inputEvent));  
        萬事具備了,就可以通知Java層的InputQueue來處理這個鍵盤事件了:
  1. env->CallStaticVoidMethod(gInputQueueClassInfo.clazz,  
  2.         dispatchMethodId, inputHandlerObjLocal, inputEventObj,  
  3.         jlong(finishedToken));  
Step 24. InputQueue.dispatchKeyEvent

        這個函數定義在frameworks/base/core/java/android/view/InputQueue.java文件中:

  1. public final class InputQueue {  
  2.     ......  
  3.   
  4.     private static void dispatchKeyEvent(InputHandler inputHandler,  
  5.             KeyEvent event, long finishedToken) {  
  6.         Runnable finishedCallback = FinishedCallback.obtain(finishedToken);  
  7.         inputHandler.handleKey(event, finishedCallback);  
  8.     }  
  9.   
  10.     ......  
  11. }  
        這個函數首先會創建一個FinishedCallback類型的對象finishedCallback,FinishedCallback是InputQueue的一個內部類,它繼承於Runnable類。這個finishedCallback對象是提供給當前Activity窗口的,當它處理完畢鍵盤事件後,需要通過消息分發的方式來回調這個finishedCallback對象,以及InputQueue類處理一個手尾的工作,後面我們會分析到。

        這裏的inputHandler對象是在前面分析應用程序註冊鍵盤消息接收通道的過程時,在Step 1(ViewRoot.setView)中傳進來的:

  1. InputQueue.registerInputChannel(mInputChannel, mInputHandler,  
  2.     Looper.myQueue());  
         它是ViewRoot類的一個成員變量mInputHandler。因此,這裏將調用ViewRoot類的內部對象mInputHandler的成員函數handleKey來處理鍵盤事件。

         Step 25. InputHandler.handleKey

         這個函數定義在frameworks/base/core/java/android/view/ViewRoot.java文件中:

  1. public final class ViewRoot extends Handler implements ViewParent,  
  2.         View.AttachInfo.Callbacks {  
  3.     ......  
  4.   
  5.     private final InputHandler mInputHandler = new InputHandler() {  
  6.         public void handleKey(KeyEvent event, Runnable finishedCallback) {  
  7.             startInputEvent(finishedCallback);  
  8.             dispatchKey(event, true);  
  9.         }  
  10.         ......  
  11.     };  
  12.   
  13.     ......  
  14. }  
         這個函數首先調用其外部類ViewRoot的startInputEvent成員函數來把回調對象finishedCallback保存下來:
  1. public final class ViewRoot extends Handler implements ViewParent,  
  2.         View.AttachInfo.Callbacks {  
  3.     ......  
  4.   
  5.     private void startInputEvent(Runnable finishedCallback) {  
  6.         ......  
  7.   
  8.         mFinishedCallback = finishedCallback;  
  9.     }  
  10.   
  11.     ......  
  12. }  
        然後再調用其外部類ViewRoot的dispatchKey成員函數來進一步處這個鍵盤事件。

        Step 26. ViewRoot.dispatchKey

        這個函數定義在frameworks/base/core/java/android/view/ViewRoot.java文件中:

  1. public final class ViewRoot extends Handler implements ViewParent,  
  2.         View.AttachInfo.Callbacks {  
  3.     ......  
  4.   
  5.     private void dispatchKey(KeyEvent event, boolean sendDone) {  
  6.         ......  
  7.   
  8.         Message msg = obtainMessage(DISPATCH_KEY);  
  9.         msg.obj = event;  
  10.         msg.arg1 = sendDone ? 1 : 0;  
  11.   
  12.         ......  
  13.   
  14.         sendMessageAtTime(msg, event.getEventTime());  
  15.     }  
  16.   
  17.     ......  
  18. }  
        ViewRoot不是直接處理這個鍵盤事件,而是把作爲一個消息(DISPATCH_KEY)它放到消息隊列中去處理,這個消息最後由ViewRoot類的deliverKeyEvent成員函數來處理。
        Step 27. ViewRoot.deliverKeyEvent

        這個函數定義在frameworks/base/core/java/android/view/ViewRoot.java文件中:

  1. public final class ViewRoot extends Handler implements ViewParent,  
  2.         View.AttachInfo.Callbacks {  
  3.     ......  
  4.   
  5.     private void deliverKeyEvent(KeyEvent event, boolean sendDone) {  
  6.         // If mView is null, we just consume the key event because it doesn't  
  7.         // make sense to do anything else with it.  
  8.         boolean handled = mView != null  
  9.             ? mView.dispatchKeyEventPreIme(event) : true;  
  10.         ......  
  11.   
  12.         // If it is possible for this window to interact with the input  
  13.         // method window, then we want to first dispatch our key events  
  14.         // to the input method.  
  15.         if (mLastWasImTarget) {  
  16.             InputMethodManager imm = InputMethodManager.peekInstance();  
  17.             if (imm != null && mView != null) {  
  18.                 ......  
  19.   
  20.                 imm.dispatchKeyEvent(mView.getContext(), seq, event,  
  21.                     mInputMethodCallback);  
  22.                 return;  
  23.             }  
  24.         }  
  25.         ......  
  26.     }  
  27.   
  28.     ......  
  29. }  
        ViewRoot在把這個鍵盤事件分發給當前激活的Activity窗口處理之前,首先會調用InputMethodManager的dispatchKeyEvent成員函數來處理這個鍵盤事件。InputMethodManager處理完這個鍵盤事件後,再回調用這裏的mInputMethodCallback對象的finishedEvent成員函數來把鍵盤事件分發給當前激活的Activity窗口處理。當然,在把這個鍵盤事件分發給InputMethodManager處理之前,ViewRoot也會先把這個鍵盤事件分發給當前激活的Activity窗口的dispatchKeyEventPreIme成員函數處理。

        Step 28. InputMethodManager.dispatchKeyEvent

        這個函數定義在frameworks/base/core/java/android/view/inputmethod/InputMethodManager.java文件中。這是一個輸入法相關的類,我們這裏就不關注了,只要知道當輸入法處理完成之後,它就會調用ViewRoot類的mInputMehtodCallback對象的finishedEvent成員函數。

        Step 29.  InputMethodCallack.finishedEvent

        這個函數定義在frameworks/base/core/java/android/view/ViewRoot.java文件中:

  1. public final class ViewRoot extends Handler implements ViewParent,  
  2.         View.AttachInfo.Callbacks {  
  3.     ......  
  4.   
  5.     static class InputMethodCallback extends IInputMethodCallback.Stub {  
  6.         private WeakReference<ViewRoot> mViewRoot;  
  7.   
  8.         public InputMethodCallback(ViewRoot viewRoot) {  
  9.                 mViewRoot = new WeakReference<ViewRoot>(viewRoot);  
  10.         }  
  11.   
  12.         public void finishedEvent(int seq, boolean handled) {  
  13.             final ViewRoot viewRoot = mViewRoot.get();  
  14.             if (viewRoot != null) {  
  15.                 viewRoot.dispatchFinishedEvent(seq, handled);  
  16.             }  
  17.         }  
  18.   
  19.         ......  
  20.     }  
  21.   
  22.     ......  
  23. }  
         這個函數最終調用ViewRoot的dispatchFinishedEvent來進一步處理。

         Step 30. ViewRoot.dispatchFinishedEvent

         這個函數定義在frameworks/base/core/java/android/view/ViewRoot.java文件中:

  1. public final class ViewRoot extends Handler implements ViewParent,  
  2.         View.AttachInfo.Callbacks {  
  3.     ......  
  4.   
  5.     public void dispatchFinishedEvent(int seq, boolean handled) {  
  6.         Message msg = obtainMessage(FINISHED_EVENT);  
  7.         msg.arg1 = seq;  
  8.         msg.arg2 = handled ? 1 : 0;  
  9.         sendMessage(msg);  
  10.     }  
  11.   
  12.     ......  
  13. }  
        和前面的Step 26一樣,ViewRoot不是直接處理這個鍵盤事件,而是把它作爲一個消息(FINISHED_EVENT)放在消息隊列中去,最後,這個消息由ViewRoot的handleFinishedEvent函數來處理。

        Step 31. ViewRoot.handleFinishedEvent

         這個函數定義在frameworks/base/core/java/android/view/ViewRoot.java文件中:

  1. public final class ViewRoot extends Handler implements ViewParent,  
  2.         View.AttachInfo.Callbacks {  
  3.     ......  
  4.   
  5.     void handleFinishedEvent(int seq, boolean handled) {  
  6.         final KeyEvent event = (KeyEvent)retrievePendingEvent(seq);  
  7.         ......  
  8.   
  9.         if (event != null) {  
  10.             final boolean sendDone = seq >= 0;  
  11.             if (!handled) {  
  12.                 deliverKeyEventToViewHierarchy(event, sendDone);  
  13.                 return;  
  14.             } else if (sendDone) {  
  15.                 ......  
  16.             } else {  
  17.                 ......  
  18.             }  
  19.         }  
  20.     }  
  21.   
  22.     ......  
  23. }  
        如果InputMethodManager沒有處理這個鍵盤事件,那麼ViewRoot就會調用deliverKeyEventToViewHierarchy函數來把這個鍵盤事件分發給當前激活的Activity窗口來處理。

        Step 32. ViewRoot.deliverKeyEventToViewHierarchy

        這個函數定義在frameworks/base/core/java/android/view/ViewRoot.java文件中:

  1. public final class ViewRoot extends Handler implements ViewParent,  
  2.         View.AttachInfo.Callbacks {  
  3.     ......  
  4.   
  5.     private void deliverKeyEventToViewHierarchy(KeyEvent event, boolean sendDone) {  
  6.         try {  
  7.             if (mView != null && mAdded) {  
  8.                 ......  
  9.   
  10.                 boolean keyHandled = mView.dispatchKeyEvent(event);  
  11.             }  
  12.   
  13.             ......  
  14.         } finally {  
  15.             if (sendDone) {  
  16.                 finishInputEvent();  
  17.             }  
  18.         }  
  19.     }  
  20.   
  21.     ......  
  22. }  
        這個函數首先會調用ViewRoot類的成員變量mView的dispatchKeyEvent來處理這個鍵盤事件,然後最調用ViewRoot類的finishInputEvent來處理手尾工作。

        ViewRoot類的成員變量mView的類型爲DecorView,它是由ActivityThread類第一次Resume當前的Activity窗口時創建的,具體可以參考ActivityThread類的handleResumeActivity成員函數,這裏就不關注了。

        Step 33. DecorView.dispatchKeyEvent

        這個函數定義在frameworks/base/policy/src/com/android/internal/policy/impl/PhoneWindow.java文件中,它是PhoneWindow類的一個內部類:

  1. public class PhoneWindow extends Window implements MenuBuilder.Callback {  
  2.     ......  
  3.   
  4.     private final class DecorView extends FrameLayout implements RootViewSurfaceTaker {  
  5.         ......  
  6.   
  7.         @Override  
  8.         public boolean dispatchKeyEvent(KeyEvent event) {  
  9.             ......  
  10.   
  11.             final Callback cb = getCallback();  
  12.             final boolean handled = cb != null && mFeatureId < 0 ? cb.dispatchKeyEvent(event)  
  13.                 : super.dispatchKeyEvent(event);  
  14.   
  15.             ......  
  16.         }  
  17.   
  18.         ......  
  19.     }  
  20.   
  21.     ......  
  22. }  
        這裏通過getCallback函數返回的是當前應用程序的激活的Activity窗口的Window.Callback接口,一般它不爲NULL,因此,這個函數會調用Activity類的dispatchKeyEvent來處理這個鍵盤事件。
        Step 34. Activity.dispatchKeyEvent

        這個函數定義在frameworks/base/core/java/android/app/Activity.java文件中:

  1. public class Activity extends ContextThemeWrapper  
  2.         implements LayoutInflater.Factory,  
  3.         Window.Callback, KeyEvent.Callback,  
  4.         OnCreateContextMenuListener, ComponentCallbacks {  
  5.     ......  
  6.   
  7.     public boolean dispatchKeyEvent(KeyEvent event) {  
  8.         ......  
  9.   
  10.         View decor = mDecor;  
  11.         if (decor == null) decor = win.getDecorView();  
  12.         return event.dispatch(this, decor != null  
  13.             ? decor.getKeyDispatcherState() : nullthis);  
  14.     }  
  15.   
  16.     ......  
  17. }  
         這裏,Activity不是直接處理這個鍵盤事件,而是通過KeyEvent的dispatch轉發一下。注意,KeyEvent的成中函數dispatch的第一個參數的類型是KeyEvent.Callback,而Activity實現了這個接口,因此,這裏可以傳this引用過去。

         Step 35. KeyEvent.dispatch

         這個函數定義在frameworks/base/core/java/android/view/KeyEvent.java文件中:

  1. public class KeyEvent extends InputEvent implements Parcelable {  
  2.     ......  
  3.   
  4.     public final boolean dispatch(Callback receiver, DispatcherState state,  
  5.             Object target) {  
  6.         switch (mAction) {  
  7.         case ACTION_DOWN: {  
  8.             ......  
  9.             boolean res = receiver.onKeyDown(mKeyCode, this);  
  10.             ......  
  11.             return res;  
  12.         }  
  13.         case ACTION_UP:  
  14.             ......  
  15.             return receiver.onKeyUp(mKeyCode, this);  
  16.         case ACTION_MULTIPLE:  
  17.             final int count = mRepeatCount;  
  18.             final int code = mKeyCode;  
  19.             if (receiver.onKeyMultiple(code, count, this)) {  
  20.                 return true;  
  21.             }  
  22.             ......  
  23.             return false;  
  24.         }  
  25.         return false;  
  26.     }  
  27.   
  28.     ......  
  29. }  
         這裏就根據一個鍵是按下(ACTION_DOWN)、還是鬆開(ACTION_UP)或者是一個相同的鍵被多次按下和鬆開(ACTION_MULTIPLE)等不同事件類型來分別調用Activity的onKeyDown、onKeyUp和onKeyMultiple函數了。
         Activity窗口處理完這個鍵盤事件後,層層返回,最後回到Step 32中,調用finishInputEvent事件來處理一些手尾工,下面我們將會看到這些手尾工是什麼。

         Step 36. ViewRoot.finishInputEvent

         這個函數定義在frameworks/base/core/java/android/view/ViewRoot.java文件中:

  1. public final class ViewRoot extends Handler implements ViewParent,  
  2.         View.AttachInfo.Callbacks {  
  3.     ......  
  4.   
  5.     private void finishInputEvent() {  
  6.         ......  
  7.   
  8.         if (mFinishedCallback != null) {  
  9.             mFinishedCallback.run();  
  10.             mFinishedCallback = null;  
  11.         } else {  
  12.             ......  
  13.         }  
  14.     }  
  15.   
  16.     ......  
  17. }  

         ViewRoot類裏面的成員變量mFinishedCallback是在前面Step 25中由InputQueue設置的,它是一個Runnable對象,實際類型是定義在InputQueue的內部類FinishedCallback,因此,這裏調用它的run方法時,接下來就會調用InputQueue的內部類FinishedCallback的run成員函數:

  1. public final class InputQueue {  
  2.     ......  
  3.   
  4.     private static class FinishedCallback implements Runnable {  
  5.         ......  
  6.   
  7.         public void run() {  
  8.             synchronized (sLock) {  
  9.                 ......  
  10.   
  11.                 nativeFinished(mFinishedToken);  
  12.                   
  13.                 ......  
  14.             }  
  15.         }  
  16.   
  17.         ......  
  18.     }  
  19.   
  20.     ......  
  21. }  
        這裏它調用外部類InputQueue的本地方法nativeFinished來進一步處理。

        Step 37.  InputQueue.nativeFinished

        這個函數定義在frameworks/base/core/jni/android_view_InputQueue.cpp文件中:

  1. static void android_view_InputQueue_nativeFinished(JNIEnv* env, jclass clazz,  
  2.         jlong finishedToken) {  
  3.     status_t status = gNativeInputQueue.finished(  
  4.         env, finishedToken, false /*ignoreSpuriousFinish*/);  
  5.   
  6.     ......  
  7. }  
        這個函數只是簡單隻調用NativeInputQueue的finished方法來進一處處理。

        Step 38. NativeInputQueue.finished

        這個函數定義在frameworks/base/core/jni/android_view_InputQueue.cpp文件中:

  1. status_t NativeInputQueue::finished(JNIEnv* env, jlong finishedToken, bool ignoreSpuriousFinish) {  
  2.     int32_t receiveFd;  
  3.     uint16_t connectionId;  
  4.     uint16_t messageSeqNum;  
  5.     parseFinishedToken(finishedToken, &receiveFd, &connectionId, &messageSeqNum);  
  6.   
  7.     { // acquire lock  
  8.         AutoMutex _l(mLock);  
  9.   
  10.         ssize_t connectionIndex = mConnectionsByReceiveFd.indexOfKey(receiveFd);  
  11.         ......  
  12.   
  13.         sp<Connection> connection = mConnectionsByReceiveFd.valueAt(connectionIndex);  
  14.         ......  
  15.   
  16.         connection->messageInProgress = false;  
  17.   
  18.         status_t status = connection->inputConsumer.sendFinishedSignal();  
  19.           
  20.         ......  
  21.     } // release lock  
  22.   
  23.     return OK;  
  24. }  
        這個函數最重要的參數便是finishedToken了,通過它可以獲得之前通知Java層的InputQueue類來處理鍵盤事件的Connection對象,它的值是在上面的Step 21(NativeInputQueue.handleReceiveCallback)中生成的:
  1. finishedToken = generateFinishedToken(receiveFd, connection->id, connection->messageSeqNum);  
        函數generateFinishedToken的定義如下:
  1. jlong NativeInputQueue::generateFinishedToken(int32_t receiveFd, uint16_t connectionId,  
  2.         uint16_t messageSeqNum) {  
  3.     return (jlong(receiveFd) << 32) | (jlong(connectionId) << 16) | jlong(messageSeqNum);  
  4. }  
        它的實現很簡單,只是把receiveFd(前向管道的讀端文件描述符)、connectionId(Client端的InputChannel對應的Connection對象在NativeInputQueue中的索引)和messageSeqNum(鍵盤消息的序列號)三個數值通過移位的方式編碼在一個jlong值裏面,即編碼在上面的finishedToken參數裏面。

        因此,在上面的finished函數裏面,首先就是要對參數值finishedToken進行解碼,把receiveFd、connectionId和messageSeqNum三個值分別取回來:

  1. parseFinishedToken(finishedToken, &receiveFd, &connectionId, &messageSeqNum);  
       parseFinishedToken的定義如下:
  1. void NativeInputQueue::parseFinishedToken(jlong finishedToken,  
  2.         int32_t* outReceiveFd, uint16_t* outConnectionId, uint16_t* outMessageIndex) {  
  3.     *outReceiveFd = int32_t(finishedToken >> 32);  
  4.     *outConnectionId = uint16_t(finishedToken >> 16);  
  5.     *outMessageIndex = uint16_t(finishedToken);  
  6. }  
       有了這個receiveFd和connectionId之後,就可以把相應的Connection對象取回來了:
  1. ssize_t connectionIndex = mConnectionsByReceiveFd.indexOfKey(receiveFd);  
  2.         ......  
  3.   
  4. sp<Connection> connection = mConnectionsByReceiveFd.valueAt(connectionIndex);  
       接下來就是調用這個connection對象中的inputConsumer對象來發送信號通知Server端的InputChannel,應用程序這一側處理完剛纔發生的鍵盤事件了:
  1. status_t status = connection->inputConsumer.sendFinishedSignal();  

       Step 39. InputConsumer.sendFinishedSignal

       這個函數定義在frameworks/base/libs/ui/InputTransport.cpp文件中:

  1. status_t InputConsumer::sendFinishedSignal() {  
  2.     ......  
  3.   
  4.     return mChannel->sendSignal(INPUT_SIGNAL_FINISHED);  
  5. }  
        這個函數的實現很簡單,只是調用其內部對象mChannel的sendSignal函數來執行發送信號的通知。前面我們已經說過,這裏的mChannel的類型爲InputChannel,它是註冊在應用程序一側的Client端InputChannel,它的成員函數sendSignal的定義我們在上面的Step 20中已經分析過了,這裏不再詳述,不過,這裏和上面Step 20不一樣的地方是,它裏的通知方向是從反向管道的寫端(在應用程序這一側)到反向管道的讀端(在InputDispatcher這一側)。

        前面我們在分析應用程序註冊鍵盤消息接收通道的過程時,在Step 18(InputDispatcher.registerInputChannel)中,說到InputDispatcher把一個反向管道的讀端文件描述符添加到WindowManagerService所運行的線程中的Looper對象中去,然後就會在這個反向管道的讀端上睡眠等待有這個管道有新的內容可讀。現在,InputConsumer往這個反向管道寫入新的內容了,於是,InputDispatcher就被喚醒過來了,喚醒過來後,它所調用的函數是InputDispatcher.handleReceiveCallback函數,這與前面的Step 21的邏輯是一樣的。

       Step 40. InputDispatcher.handleReceiveCallack

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

  1. int InputDispatcher::handleReceiveCallback(int receiveFd, int events, void* data) {  
  2.     InputDispatcher* d = static_cast<InputDispatcher*>(data);  
  3.   
  4.     { // acquire lock  
  5.         AutoMutex _l(d->mLock);  
  6.   
  7.         ssize_t connectionIndex = d->mConnectionsByReceiveFd.indexOfKey(receiveFd);  
  8.         ......  
  9.   
  10.         nsecs_t currentTime = now();  
  11.   
  12.         sp<Connection> connection = d->mConnectionsByReceiveFd.valueAt(connectionIndex);  
  13.         ......  
  14.   
  15.         status_t status = connection->inputPublisher.receiveFinishedSignal();  
  16.         if (status) {  
  17.             ......  
  18.             return 0; // remove the callback  
  19.         }  
  20.   
  21.         d->finishDispatchCycleLocked(currentTime, connection);  
  22.         ......  
  23.   
  24.         return 1;  
  25.     } // release lock  
  26. }  
        這個函數首先是通過傳進來的receiveFd參數(反向管道的讀端文件描述符)的值取得相應的Connection對象:
  1. ssize_t connectionIndex = d->mConnectionsByReceiveFd.indexOfKey(receiveFd);  
  2.      ......  
  3.   
  4. sp<Connection> connection = d->mConnectionsByReceiveFd.valueAt(connectionIndex);  
        然後通過調用這個connection對象的內部對象inputPublisher的receiveFinishedSignal函數來確認是否真的收到鍵盤事件處理完成的信號,確認之後,就會調用InputDispatcher對象d的finishDispatchCycleLocked函數來執行一些善後工作。下面我們就依次分析這兩個過程。

        Step 41. InputPublisher.receiverFinishedSignal

        這個函數定義在frameworks/base/libs/ui/InputTransport.cpp文件中:

  1. status_t InputPublisher::receiveFinishedSignal() {  
  2.     ....  
  3.   
  4.     char signal;  
  5.     status_t result = mChannel->receiveSignal(& signal);  
  6.     if (result) {  
  7.         return result;  
  8.     }  
  9.     if (signal != INPUT_SIGNAL_FINISHED) {  
  10.         .......  
  11.         return UNKNOWN_ERROR;  
  12.     }  
  13.     return OK;  
  14. }  
        這裏的邏輯和前面的Step 22中NativeInputQueue確認是否真的收到鍵盤事件分發的信號的邏輯是一致的,都是通過InputChannel的receiveSignal函數來確認是否在管道中收到了某一個約定的字符值,不過,這裏約定的字符值爲INPUT_SIGNAL_FINISHED。

        回到前面的Step 40中,確認了是真的收到了鍵盤事件處理完成的信號後,就調用InputDispatcher的finishDispatchCycleLocked函數來執行一些善後工作了。

        Step 42. InputDispatcher.finishDispatchCycleLocked
        這個函數定義在frameworks/base/libs/ui/InputDispatcher.cpp文件中:

  1. void InputDispatcher::finishDispatchCycleLocked(nsecs_t currentTime,  
  2.         const sp<Connection>& connection) {  
  3.     ......  
  4.   
  5.     // Notify other system components.  
  6.     onDispatchCycleFinishedLocked(currentTime, connection);  
  7.   
  8.     // Reset the publisher since the event has been consumed.  
  9.     // We do this now so that the publisher can release some of its internal resources  
  10.     // while waiting for the next dispatch cycle to begin.  
  11.     status_t status = connection->inputPublisher.reset();  
  12.     ......  
  13.   
  14.     startNextDispatchCycleLocked(currentTime, connection);  
  15. }  
        這個函數主要就是做了三件事情:

       一是通知其它系統,InputDispatcher完成了一次鍵盤事件的處理:

  1. // Notify other system components.  
  2. onDispatchCycleFinishedLocked(currentTime, connection);  
        二是調用相應的connection對象的內部對象inputPublisher來的reset函數來回收一些資源,它裏面其實就是釋放前面在Step 18(InputPublisher.publishKeyEvent)使用的匿名共享內存了:
  1. // Reset the publisher since the event has been consumed.  
  2. // We do this now so that the publisher can release some of its internal resources  
  3. // while waiting for the next dispatch cycle to begin.  
  4. status_t status = connection->inputPublisher.reset();  
        三是調用InputDispatcher的startNextDispatchCycleLocked函數來處理下一個鍵盤事件:
  1. startNextDispatchCycleLocked(currentTime, connection);  
        因爲正在處理當前這個鍵盤事件的時候,很有可能又同時發生了其它的鍵盤事件,因此,這裏InputDispatcher還不能停下來,需要繼續調用startNextDispatchCycleLocked繼續處理鍵盤事件,不過下一個鍵盤事件的處理過程和我們現在分析的過程就是一樣的了。

        至此,InputManager分發鍵盤消息給應用程序的過程就分析完成了,這是一個比較複雜的過程,不過,只要我們抓住主要的線索,就不難理解了,現在我們就小結一下這個過程的四個主要線索:

        A. 鍵盤事件發生,InputManager中的InputReader被喚醒,此前InputReader睡眠在/dev/input/event0這個設備文件上;

        B. InputReader被喚醒後,它接着喚醒InputManager中的InputDispatcher,此前InputDispatcher睡眠在InputManager所運行的線程中的Looper對象裏面的管道的讀端上;

        C. InputDispatcher被喚醒後,它接着喚醒應用程序的主線程來處理這個鍵盤事件,此前應用程序的主線程睡眠在Client端InputChannel中的前向管道的讀端上;

        D. 應用程序處理處理鍵盤事件之後,它接着喚醒InputDispatcher來執行善後工作,此前InputDispatcher睡眠在Server端InputChannel的反向管道的讀端上,注意這裏與第二個線索處的區別。

        4. 應用程序註銷鍵盤消息接收通道的過程分析
        當Activity窗口創建時,它會向InputManager註冊鍵盤消息接收通道,而當Activity窗口銷燬時,它就會向InputManager註銷前面註冊的鍵盤消息接收通道了,本節內容就來看看應用程序註銷鍵盤消息接收通道的過程。

        當我們按下鍵盤上的Back鍵時,當前激活的Activity窗口就會被失去焦點,但是這時候它還沒有被銷燬,它的狀態被設置爲Stopped;當新的Activity窗口即將要顯示時,它會通知WindowManagerService,這時候WindowManagerService就會處理當前處理Stopped狀態的Activity窗口了,要執行的操作就是銷燬它們了,在銷燬的時候,就會註銷它們之前所註冊的鍵盤消息接收通道。

        新的Activity窗口通知WindowManagerService它即將要顯示的過程比較複雜,但是它與我們本節要介紹的內容不是很相關,因此,這裏就略過大部過程了,我們從ActvitiyRecord的windowsVisible函數開始分析。注意,這裏的ActivityRecord是新的Activity窗口在ActivityManangerService的代表,而那些處於Stopped狀態的Activity窗口

會放在ActivityStack類的一個等待可見的mWaitingVisibleActivities列表裏面,事實於,對於那些Stopped狀態的Activity窗口來說,它們是等待銷燬,而不是等待可見。

        像前面一樣,我們先來看一張應用程序註銷鍵盤消息接收通道的過程的序列圖,然後根據這個序列圖來詳細分析互一個步驟:


點擊查看大圖

        Step 1. ActivityRecord.windowsVisible

        這個函數定義在frameworks/base/services/java/com/android/server/am/ActivityRecord.java文件中:

  1. class ActivityRecord extends IApplicationToken.Stub {  
  2.     ......  
  3.     boolean nowVisible;     // is this activity's window visible?  
  4.     boolean idle;           // has the activity gone idle?  
  5.     ......  
  6.   
  7.   
  8.     public void windowsVisible() {  
  9.         synchronized(service) {  
  10.             ......  
  11.   
  12.             if (!nowVisible) {  
  13.                 nowVisible = true;  
  14.                 if (!idle) {  
  15.                     .......  
  16.                 } else {  
  17.                     // If this activity was already idle, then we now need to  
  18.                     // make sure we perform the full stop of any activities  
  19.                     // that are waiting to do so.  This is because we won't  
  20.                     // do that while they are still waiting for this one to  
  21.                     // become visible.  
  22.                     final int N = stack.mWaitingVisibleActivities.size();  
  23.                     if (N > 0) {  
  24.                         for (int i=0; i<N; i++) {  
  25.                             ActivityRecord r = (ActivityRecord)  
  26.                                 stack.mWaitingVisibleActivities.get(i);  
  27.                             r.waitingVisible = false;  
  28.                             ......  
  29.                         }  
  30.                         stack.mWaitingVisibleActivities.clear();  
  31.   
  32.                         Message msg = Message.obtain();  
  33.                         msg.what = ActivityStack.IDLE_NOW_MSG;  
  34.                         stack.mHandler.sendMessage(msg);  
  35.   
  36.                     }  
  37.                 }  
  38.                 ......  
  39.             }  
  40.         }  
  41.     }  
  42.   
  43.     ......  
  44. }  
        應用程序中的每一個Activity在ActivityManagerService都有一個代表ActivityRecord,它們以堆棧的形式組織在ActivityManaerService中的ActivityStack中。一個即將要顯示,但是還沒有顯示的Activity,它在ActivityManagerService中的ActivityRecord的成員變量nowVisible爲false,而成員變量idle爲ture,表示這個即將要顯示的Activity窗口處於空閒狀態。因此,在上面的這個函數中,會執行下面的語句:
  1. final int N = stack.mWaitingVisibleActivities.size();  
  2. if (N > 0) {  
  3.     for (int i=0; i<N; i++) {  
  4.         ActivityRecord r = (ActivityRecord)  
  5.         stack.mWaitingVisibleActivities.get(i);  
  6.         r.waitingVisible = false;  
  7.         ......  
  8.     }  
  9.     stack.mWaitingVisibleActivities.clear();  
  10.   
  11.     Message msg = Message.obtain();  
  12.     msg.what = ActivityStack.IDLE_NOW_MSG;  
  13.     stack.mHandler.sendMessage(msg);  
  14.   
  15. }  
        前面我們說過,當用戶按下鍵盤上的Back鍵時,當前激活的Activity記錄就被放在ActivityStack對象stack的成員變量mWaitingVisibleActivities中了,這時候就要對它進行處理了。首先是將它們的Activity記錄的waitingVisible設置爲false,然後就把它們從ActivityStack對象stack的成員變量mWaitingVisibleActivities清空,最後向ActivityStack對象stack發送一個ActivityStack.IDLE_NOW_MSG消息。這個消息最終是由ActivityStack類的activityIdleInternal函數來處理的。

        Step 2. ActivityStack.activityIdleInternal

        這個函數定義在frameworks/base/services/java/com/android/server/am/ActivityStack.java文件中:

  1. public class ActivityStack {  
  2.     ......  
  3.   
  4.     final void activityIdleInternal(IBinder token, boolean fromTimeout,  
  5.             Configuration config) {  
  6.         ......  
  7.   
  8.         ArrayList<ActivityRecord> stops = null;  
  9.         ......  
  10.   
  11.         int NS = 0;  
  12.         ......  
  13.   
  14.         synchronized (mService) {  
  15.             ......  
  16.   
  17.             // Atomically retrieve all of the other things to do.  
  18.             stops = processStoppingActivitiesLocked(true);  
  19.             NS = stops != null ? stops.size() : 0;  
  20.             ......  
  21.         }  
  22.   
  23.         int i;  
  24.   
  25.         ......  
  26.   
  27.         // Stop any activities that are scheduled to do so but have been  
  28.         // waiting for the next one to start.  
  29.         for (i=0; i<NS; i++) {  
  30.             ActivityRecord r = (ActivityRecord)stops.get(i);  
  31.             synchronized (mService) {  
  32.                 if (r.finishing) {  
  33.                     finishCurrentActivityLocked(r, FINISH_IMMEDIATELY);  
  34.                 } else {  
  35.                     ......  
  36.                 }  
  37.             }  
  38.         }  
  39.   
  40.         ......  
  41.     }  
  42.   
  43.     ......  
  44. }  
        這個函數首先會調用processStoppingActivitiesLocked函數把所有處於Stopped狀態的Activity取回來,然後逐個分析它們,如果它們的ActivityRecord中的finishing成員變量爲true,就說明這個Activity需要銷燬了,於是,就調用finishCurrentActivityLocked函數來銷燬它們。

        Step 3. ActivityStack.finishCurrentActivityLocked

        這個函數定義在frameworks/base/services/java/com/android/server/am/ActivityStack.java文件中:

  1. public class ActivityStack {  
  2.     ......  
  3.   
  4.     private final ActivityRecord finishCurrentActivityLocked(ActivityRecord r,  
  5.             int mode) {  
  6.         ......  
  7.   
  8.         return finishCurrentActivityLocked(r, index, mode);  
  9.     }  
  10.   
  11.     private final ActivityRecord finishCurrentActivityLocked(ActivityRecord r,  
  12.             int index, int mode) {  
  13.         ......  
  14.   
  15.         // make sure the record is cleaned out of other places.  
  16.         mStoppingActivities.remove(r);  
  17.         mWaitingVisibleActivities.remove(r);  
  18.         ......  
  19.   
  20.         final ActivityState prevState = r.state;  
  21.         r.state = ActivityState.FINISHING;  
  22.   
  23.         if (mode == FINISH_IMMEDIATELY  
  24.             || prevState == ActivityState.STOPPED  
  25.             || prevState == ActivityState.INITIALIZING) {  
  26.             // If this activity is already stopped, we can just finish  
  27.             // it right now.  
  28.             return destroyActivityLocked(r, true) ? null : r;  
  29.         } else {  
  30.             ......  
  31.         }  
  32.   
  33.         return r;  
  34.     }  
  35.   
  36.     ......  
  37. }  
        從上面的Step 2中傳進來的參數mode爲FINISH_IMMEDIATELY,並且這個即將要被銷燬的Activity的狀態爲Stopped,因此,接下來就會調用destroyActivityLocked函數來銷燬它。

        Step 4. ActivityStack.destroyActivityLocked

        這個函數定義在frameworks/base/services/java/com/android/server/am/ActivityStack.java文件中:

  1. public class ActivityStack {  
  2.     ......  
  3.   
  4.     final boolean destroyActivityLocked(ActivityRecord r,  
  5.             boolean removeFromApp) {  
  6.         ......  
  7.   
  8.         boolean removedFromHistory = false;  
  9.   
  10.         ......  
  11.   
  12.         final boolean hadApp = r.app != null;  
  13.   
  14.         if (hadApp) {  
  15.             ......  
  16.   
  17.             try {  
  18.                 ......  
  19.                 r.app.thread.scheduleDestroyActivity(r, r.finishing,  
  20.                     r.configChangeFlags);  
  21.             } catch (Exception e) {  
  22.                 ......  
  23.             }  
  24.   
  25.             ......  
  26.         } else {  
  27.             ......  
  28.         }  
  29.   
  30.         ......  
  31.   
  32.         return removedFromHistory;  
  33.     }  
  34.   
  35.     ......  
  36. }  
         在前面一篇文章Android應用程序啓動過程源代碼分析中,我們說到,每一個應用程序進程在ActivityManagerService中,都ProcessRecord記錄與之對應,而每一個Activity,都是運行在一個進程上下文中,因此,在ActivityManagerService中,每一個ActivityRecord的app成員變量都應該指向一個ProcessRecord記錄,於是,這裏得到的hadApp爲true。在ProcessRecord類中,有一個成員變量thread,它的類型爲IApplicationThread。在文章Android應用程序啓動過程源代碼分析中,我們也曾經說過,每一個應用程序在啓動的時候,它都會在內部創建一個ActivityThread對象,而在這個ActivityThread對象中,有一個成員變量mAppThread,它的類型爲ApplicationThread,這是一個Binder對象,專門用來負責在應用程序和ActivityManagerService之間執行進程間通信工作的。應用程序在啓動的時候,就會將這個Binder對象傳遞給ActivityManagerService,而ActivityManagerService就會把它保存在相應的ProcessRecord記錄的thread成員變量中。因此,ProcessRecord記錄的thread成員變量其實就是ApplicationThread對象的遠程接口,於是,執行下面這個語句的時候:
  1. r.app.thread.scheduleDestroyActivity(r, r.finishing,  
  2.     r.configChangeFlags);  
        就會進入到ApplicationThread類中的scheduleDestroyActivity函數來。

        Step 5. ApplicationThread.scheduleDestroyActivity

        這個函數定義在frameworks/base/core/java/android/app/ActivityThread.java文件中:

  1. public final class ActivityThread {  
  2.     ......  
  3.   
  4.     private final class ApplicationThread extends ApplicationThreadNative {  
  5.         ......  
  6.   
  7.         public final void scheduleDestroyActivity(IBinder token, boolean finishing,  
  8.                 int configChanges) {  
  9.   
  10.             queueOrSendMessage(H.DESTROY_ACTIVITY, token, finishing ? 1 : 0,  
  11.                     configChanges);  
  12.         }  
  13.   
  14.         ......  
  15.     }  
  16.   
  17.     ......  
  18. }  
        這個函數調用外部類ActivityThread的queueOrSendMessage函數來往應用程序的消息隊列中發送一個H.DESTROY_ACTIVITY消息,這個消息最終由ActivityThread類的handleDestroyActivity函數來處理。

        Step 6. ActivityThread.handleDestroyActivity

        這個函數定義在frameworks/base/core/java/android/app/ActivityThread.java文件中:

  1. public final class ActivityThread {  
  2.     ......  
  3.   
  4.     private final void handleDestroyActivity(IBinder token, boolean finishing,  
  5.             int configChanges, boolean getNonConfigInstance) {  
  6.         ......  
  7.   
  8.         ActivityClientRecord r = performDestroyActivity(token, finishing,  
  9.             configChanges, getNonConfigInstance);  
  10.         if (r != null) {  
  11.             WindowManager wm = r.activity.getWindowManager();  
  12.             View v = r.activity.mDecor;  
  13.             if (v != null) {  
  14.                 ......  
  15.   
  16.                 if (r.activity.mWindowAdded) {  
  17.                     wm.removeViewImmediate(v);  
  18.                 }  
  19.                   
  20.                 ......  
  21.             }  
  22.             ......  
  23.         }  
  24.   
  25.         ......  
  26.     }  
  27.   
  28.     ......  
  29. }  
        這裏首先調用performDestroyActivity來執行一些銷燬Activity的操作,期間就會調用Activity的onDestroy函數讓Activity本身有機會執行一些銷燬前的工作了。這裏通過r.activity.getWindowManager函數返回的是一個LocalWindowManager對象,而通過r.activity.mDecor得到的是一個DecorView對象,這些都是在Activity啓動的時候設置好的。函數最後調用LocalWindowManager對象wm的removeViewImmediate函員來從LocalWindowManager移除這個DecorView對象。

        Step 7. LocalWindowManager.removeViewImmediate

        這個函數定義在frameworks/base/core/java/android/view/Window.java文件中:

  1. public abstract class Window {  
  2.     ......  
  3.   
  4.     private class LocalWindowManager implements WindowManager {  
  5.         ......  
  6.   
  7.         public final void removeViewImmediate(View view) {  
  8.             mWindowManager.removeViewImmediate(view);  
  9.         }  
  10.   
  11.         ......  
  12.   
  13.         private final WindowManager mWindowManager;  
  14.     }  
  15.   
  16.     ......  
  17. }  
        LocalWindowManager類的成員變量mWindowManager是一個WndowManagerImpl對象,這個函數只是簡單地調用WndowManagerImpl類的removeViewImmediate來進一步處理。

       Step 8. WndowManagerImpl.removeViewImmediate

       這個函數定義在frameworks/base/core/java/android/view/WindowManagerImpl.java文件中:

  1. public class WindowManagerImpl implements WindowManager {  
  2.     ......  
  3.   
  4.     public void removeViewImmediate(View view) {  
  5.         synchronized (this) {  
  6.             int index = findViewLocked(view, true);  
  7.             ViewRoot root = mRoots[index];  
  8.             ......  
  9.   
  10.             root.die(true);  
  11.               
  12.             ......  
  13.         }  
  14.     }  
  15.   
  16.     ......  
  17. }  
         這個函數首先是找到這個view所屬的ViewRoot對象root,然後調用這個root對象的die函數來銷燬它。

         Step 9. ViewRoot.die

         這個函數定義在frameworks/base/core/java/android/view/ViewRoot.java文件中:

  1. public final class ViewRoot extends Handler implements ViewParent,  
  2.         View.AttachInfo.Callbacks {  
  3.     ......  
  4.   
  5.     public void die(boolean immediate) {  
  6.         if (immediate) {  
  7.             doDie();  
  8.         } else {  
  9.             ......  
  10.         }  
  11.     }  
  12.       
  13.     ......  
  14. }  
        上面Step 8傳進來的immediate參數爲true,因此,這裏直接調用doDie函數來進一步處理。

        Step 10. ViewRoot.doDie

        這個函數定義在frameworks/base/core/java/android/view/ViewRoot.java文件中:

  1. public final class ViewRoot extends Handler implements ViewParent,  
  2.         View.AttachInfo.Callbacks {  
  3.     ......  
  4.   
  5.     void doDie() {  
  6.         ......  
  7.   
  8.         synchronized (this) {  
  9.             ......  
  10.   
  11.             if (mAdded) {  
  12.                 mAdded = false;  
  13.                 dispatchDetachedFromWindow();  
  14.             }  
  15.         }  
  16.     }  
  17.       
  18.     ......  
  19. }  
        當我們把Activity窗口中的View添加到一個ViewRoot對象時,就會把它的成員變量mAdded設置爲true,這樣就表示這個ViewRoot中有View存在,於是,這裏就會調用dispatchDetachedFromWindow函數來進一步處理。

        Step 11. ViewRoot.ispatchDetachedFromWindow

        這個函數定義在frameworks/base/core/java/android/view/ViewRoot.java文件中:

  1. public final class ViewRoot extends Handler implements ViewParent,  
  2.         View.AttachInfo.Callbacks {  
  3.     ......  
  4.   
  5.     void dispatchDetachedFromWindow() {  
  6.         ......  
  7.   
  8.         if (mInputChannel != null) {  
  9.             if (mInputQueueCallback != null) {  
  10.                 ......  
  11.             } else {  
  12.                 InputQueue.unregisterInputChannel(mInputChannel);  
  13.             }  
  14.         }  
  15.   
  16.         try {  
  17.             sWindowSession.remove(mWindow);  
  18.         } catch (RemoteException e) {  
  19.         }  
  20.   
  21.         ......  
  22.     }  
  23.   
  24.     ......  
  25. }  
        前面在介紹應用程序註冊鍵盤消息接收通道的過程時,在Step 18,我們說到,ViewRoot類中的mInputQueueCallback爲null,表示由這個ViewRoot自己來管理鍵盤輸入事件,因此,這裏首先會調用InputQueue的unregisterInputChannel函數來註銷註冊在應用程序這一側的Client端InputChannel,然後再調用sWindowSession的remove函數來註銷註冊在InputManager這一側的Server端InputChannel,這個邏輯是和前面介紹應用程序註冊鍵盤消息接收通道的邏輯相對應的,前面分別註冊了這兩個InputChannel,現在Activity要銷燬了,當然就要把它們註銷了。

        我們先來看註銷註冊在應用程序這一側的Client端InputChannel,然後再回過頭來分析註銷註冊在InputManager這一側的Server端InputChannel。

        Step 12. InputQueue.unregisterInputChannel

        這個函數定義在frameworks/base/core/java/android/view/InputQueue.java文件中:

  1. public final class InputQueue {  
  2.     ......  
  3.   
  4.     public static void unregisterInputChannel(InputChannel inputChannel) {  
  5.         ......  
  6.   
  7.         synchronized (sLock) {  
  8.             ......  
  9.   
  10.             nativeUnregisterInputChannel(inputChannel);  
  11.         }  
  12.     }  
  13.   
  14.     ......  
  15. }  
         這個函數只是簡單地調用本地方法nativeUnregisterInputChannel來執行具體的操作。

         Step 13. InputQueue.nativeUnregisterInputChannel
         這個函數定義在frameworks/base/core/jni/android_view_InputQueue.cpp文件中:

  1. static void android_view_InputQueue_nativeUnregisterInputChannel(JNIEnv* env, jclass clazz,  
  2.         jobject inputChannelObj) {  
  3.     status_t status = gNativeInputQueue.unregisterInputChannel(env, inputChannelObj);  
  4.   
  5.     ......  
  6. }  
        這裏調用NativeInputQueue的成員函數unregisterInputChannel來進一步處理。

        Step 14. NativeInputQueue.unregisterInputChannel
        這個函數定義在frameworks/base/core/jni/android_view_InputQueue.cpp文件中:

  1. status_t NativeInputQueue::unregisterInputChannel(JNIEnv* env, jobject inputChannelObj) {  
  2.     sp<InputChannel> inputChannel = android_view_InputChannel_getInputChannel(env,  
  3.         inputChannelObj);  
  4.     ......  
  5.   
  6.     { // acquire lock  
  7.         AutoMutex _l(mLock);  
  8.   
  9.         ssize_t connectionIndex = getConnectionIndex(inputChannel);  
  10.         ......  
  11.   
  12.         sp<Connection> connection = mConnectionsByReceiveFd.valueAt(connectionIndex);  
  13.         mConnectionsByReceiveFd.removeItemsAt(connectionIndex);  
  14.   
  15.         connection->status = Connection::STATUS_ZOMBIE;  
  16.   
  17.         connection->looper->removeFd(inputChannel->getReceivePipeFd());  
  18.   
  19.         env->DeleteGlobalRef(connection->inputHandlerObjGlobal);  
  20.         connection->inputHandlerObjGlobal = NULL;  
  21.         ......  
  22.     } // release lock  
  23.   
  24.     ......  
  25.     return OK;  
  26. }  
        真正的註銷工作就是這裏實現的了,讀者可以對照前面介紹應用程序註冊鍵盤消息接收通道過程中的Step 21(NativeInputQueue.registerInputChannel)來分析,它首先是將在之前創建的Connection對象從NativeInputQueue中的mConnectionByReceiveFd向量中刪除:
  1. ssize_t connectionIndex = getConnectionIndex(inputChannel);  
  2. ......  
  3.   
  4. sp<Connection> connection = mConnectionsByReceiveFd.valueAt(connectionIndex);  
  5. mConnectionsByReceiveFd.removeItemsAt(connectionIndex);  
        然後再把這個Client端InputChannel的前向管道的讀端文件描述符從應用程序主線程中的Looper對象中刪除:
  1. connection->looper->removeFd(inputChannel->getReceivePipeFd());  
        這樣,這個Activity窗口以後就不會接收到鍵盤事件了。

        最後將Connection對象中的回調對象inputHandlerOjbGlobal對象刪除:

  1. env->DeleteGlobalRef(connection->inputHandlerObjGlobal);  
  2. connection->inputHandlerObjGlobal = NULL;  
        回憶一下前面我們在分析InputManager分發鍵盤消息給應用程序處理時,曾經說到,每當有鍵盤事件發生時,InputManager首先就會調用NativeInputQueue類的handleReceiveCallback函數。在這個handleReceiveCallback函數裏面,NativeInputQueue會找到相應的Connection對象,然後把它裏面的內部對象inputHandlerOjbGlobal作爲參數來調用Java層的InputQueue類的dispatchKeyEvent函數來通知應用程序,有鍵盤事件發生了。在InputQueue類的dispatchKeyEvent函數裏面,就是通過這個inputHandlerOjbGlobal對象來直正通知到當前激活的Activity窗口來處理這個鍵盤事件的。

        註冊在應用程序這一側的Client端InputChannel被註銷以後,回到前面的Step 11中,我們繼續分析註銷註冊在InputManager這一側的Server端InputChannel。

        Step 15. WindowManagerService.Session.remove

        這個函數定義在frameworks/base/services/java/com/android/server/WindowManagerService.java文件中:

  1. public class WindowManagerService extends IWindowManager.Stub  
  2.         implements Watchdog.Monitor {  
  3.     ......  
  4.   
  5.     private final class Session extends IWindowSession.Stub  
  6.             implements IBinder.DeathRecipient {  
  7.         ......  
  8.   
  9.         public void remove(IWindow window) {  
  10.             removeWindow(this, window);  
  11.         }  
  12.   
  13.         ......  
  14.     }  
  15.   
  16.     ......  
  17. }  
        這個函數只是簡單地調用其外部類WindowManagerService的removeWindow函數來進一步執行操作。

        Step 16. WindowManagerService.removeWindow
        這個函數定義在frameworks/base/services/java/com/android/server/WindowManagerService.java文件中:

  1. public class WindowManagerService extends IWindowManager.Stub  
  2.         implements Watchdog.Monitor {  
  3.     ......  
  4.   
  5.     public void removeWindow(Session session, IWindow client) {  
  6.         synchronized(mWindowMap) {  
  7.             WindowState win = windowForClientLocked(session, client, false);  
  8.             if (win == null) {  
  9.                 return;  
  10.             }  
  11.             removeWindowLocked(session, win);  
  12.         }  
  13.     }  
  14.   
  15.     ......  
  16. }  

        回憶一下前面我們在分析應用程序註冊鍵盤消息管道的過程時,在Step 11(WindowManagerService.addWindow)中,WindowManagerService爲這個即將要激活的Activity窗口創建了一個WindowState對象win,創建的時候,使用了從ViewRoot中傳過來的兩個參數,分別是一個Session對象session和一個IWindow對象client。 

       在這個函數中,ViewRoot傳過來的兩個參數session和client和上面說的兩個參數是一致的,因此,這個函數首先通過參數session和client得到一個WindowState對象win,然後調用removeWindowLocked來把它從WindowManagerService刪除。

        Step 17. WindowManagerService.removeWindowLocked
        這個函數定義在frameworks/base/services/java/com/android/server/WindowManagerService.java文件中:

  1. public class WindowManagerService extends IWindowManager.Stub  
  2.         implements Watchdog.Monitor {  
  3.     ......  
  4.   
  5.     public void removeWindowLocked(Session session, WindowState win) {  
  6.         ......  
  7.   
  8.         win.disposeInputChannel();  
  9.   
  10.         ......  
  11.     }  
  12.   
  13.     ......  
  14. }  
        我們忽略了這個函數的其它邏輯,只關注註銷之前註冊的Server端InputChannel的邏輯,這裏,註銷的操作就是調用win的disposeInputChannel進行的了。

        Step 18. WindowState.disposeInputChannel

        這個函數定義在frameworks/base/services/java/com/android/server/WindowManagerService.java文件中:

  1. public class WindowManagerService extends IWindowManager.Stub  
  2.         implements Watchdog.Monitor {  
  3.     ......  
  4.   
  5.     private final class WindowState implements WindowManagerPolicy.WindowState {  
  6.         ......  
  7.   
  8.         void disposeInputChannel() {  
  9.             if (mInputChannel != null) {  
  10.                 mInputManager.unregisterInputChannel(mInputChannel);  
  11.   
  12.                 mInputChannel.dispose();  
  13.                 mInputChannel = null;  
  14.             }  
  15.         }  
  16.   
  17.         ......  
  18.     }  
  19.   
  20.     ......  
  21. }  
        上面說到,在前面分析應用程序註冊鍵盤消息管道的過程時,在Step 11(WindowManagerService.addWindow)中,爲當前這個Activity窗口創建了一個WindowState對象,接着創建了一個輸入管道後,把Server端的InputChannel保存了在這個WindowState對象的成員變量mInputChannel中,因此,這裏,就可以把它取回來,然後調用mInputManager對象的unregisterInputChannel函數來把它註銷掉了。

        Step 19. InputManager.unregisterInputChannel

        這個函數定義在frameworks/base/services/java/com/android/server/InputManager.java文件中:

  1. public class InputManager {  
  2.     ......  
  3.   
  4.     public void unregisterInputChannel(InputChannel inputChannel) {  
  5.         ......  
  6.   
  7.         nativeUnregisterInputChannel(inputChannel);  
  8.     }  
  9.   
  10.     ......  
  11. }  
         這個函數很簡單,它調用本地方法nativeUnregisterInputChannel來進一步處理。

         Step 20. InputManager.nativeUnregisterInputChannel

         這個函數定義在frameworks/base/services/jni/com_android_server_InputManager.cpp文件中:

  1. static void android_server_InputManager_nativeUnregisterInputChannel(JNIEnv* env, jclass clazz,  
  2.         jobject inputChannelObj) {  
  3.     ......  
  4.   
  5.   
  6.     sp<InputChannel> inputChannel = android_view_InputChannel_getInputChannel(env,  
  7.                         inputChannelObj);  
  8.     ......  
  9.   
  10.   
  11.     status_t status = gNativeInputManager->unregisterInputChannel(env, inputChannel);  
  12.   
  13.     ......  
  14. }  
        這個函數首先調用android_view_InputChannel_getInputChannel函數根據Java層的InputChannel對象找到C++層的InputChannel對象,然後調用NativeInputManager的unregisterInputChannel函數來執行註銷的操作。

        Step 21. NativeInputManager.unregisterInputChannel
        這個函數定義在frameworks/base/services/jni/com_android_server_InputManager.cpp文件中:

  1. status_t NativeInputManager::unregisterInputChannel(JNIEnv* env,  
  2.         const sp<InputChannel>& inputChannel) {  
  3.     ......  
  4.   
  5.     return mInputManager->getDispatcher()->unregisterInputChannel(inputChannel);  
  6. }  
       這個函數與前面分析應用程序註冊鍵盤消息通道的Step 17(NativeInputManager.registerInputChannel)相對應,主要是調用InputDispatcher對象的unregisterInputChannel函數來執行真正註銷的操作。

       Step 22. InputDispatcher.unregisterInputChannel
       這個函數定義在frameworks/base/libs/ui/InputDispatcher.cpp文件中:

  1. status_t InputDispatcher::unregisterInputChannel(const sp<InputChannel>& inputChannel) {  
  2.     ......  
  3.   
  4.     { // acquire lock  
  5.         AutoMutex _l(mLock);  
  6.   
  7.         ssize_t connectionIndex = getConnectionIndexLocked(inputChannel);  
  8.         ......  
  9.   
  10.         sp<Connection> connection = mConnectionsByReceiveFd.valueAt(connectionIndex);  
  11.         mConnectionsByReceiveFd.removeItemsAt(connectionIndex);  
  12.   
  13.         ......  
  14.   
  15.         mLooper->removeFd(inputChannel->getReceivePipeFd());  
  16.   
  17.         .....  
  18.   
  19.     } // release lock  
  20.   
  21.     ......  
  22.   
  23.     return OK;  
  24. }  
        這一步與前面的Step 14註銷應用程序一側的Client端InputChannel是差不多的,只不過這裏是從InputDispatcher中把Server端的InputChannel註銷掉。首先是根據傳進來的參數inputChannel找到它在InputDispatcher中對應的Connection對象在mConnectionsByReceiveFd中的索引,然後把它從mConnectionsByReceiveFd中刪除:
  1. ssize_t connectionIndex = getConnectionIndexLocked(inputChannel);  
  2.     ......  
  3.   
  4. sp<Connection> connection = mConnectionsByReceiveFd.valueAt(connectionIndex);  
  5. mConnectionsByReceiveFd.removeItemsAt(connectionIndex);  
        最後,還需要把這個InputChannel中的反向管道讀端文件描述符從InputDispatcher的內部對象mLooper中刪除,因爲這個文件描述符是在前面註冊Server端的InputChannel時加入到mLooper對象去的,具體可以參考上面分析應用程序註冊鍵盤消息接收通道的過程中的Step 18(InputDispatcher.registerInputChannel)。

        這樣, 應用程序註銷鍵盤消息接收通道的過程就分析完成了,整個應用程序鍵盤消息處理機制也分析完成了,這是一個比較複雜的過程,要完全理解它還需要花費一些努力和時間,不過,理解了這個過程之後,對Android應用程序框架層的理解就更進一步了。


發佈了21 篇原創文章 · 獲贊 3 · 訪問量 27萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章