1. 程式人生 > >Android系統--事件讀取及分發

Android系統--事件讀取及分發

1. 簡介

      WindowManagerService分發事件是通過它的InputManager來完成的。

      在初始化時,各部分狀態如下:

      • InputManager.InputReader:正在睡眠等待事件的發生

      • InputManager.InputDispatcher:正在等待InputReader從睡眠中醒過來並且喚醒它

      • Activity應用程式:正在訊息迴圈中等待InputDispatcher把它喚醒

      初始化之後,如果有事件發生,其呼叫流程見下面的內容。

2. 事件分發流程

2.1 Server端

  • InputReader

  1) InputReader.pollOnce (InputReader.cpp)
     被通知是否有事件可讀
  2) EventHub.getEvent (EventHub.cpp)
     讀取真正的事件
  3) InputReader.process (InputReader.cpp)
  4) InputReader.consumeEvent (InputReader.cpp)
  5) InputDevice.process (InputReader.cpp)
  6) mapper->process(rawEvent) (下面以鍵盤為例)
     則真正呼叫:  KeyboardInputMapper.process (InputReader.cpp)
  7) KeyboardInputMapper.processKey (InputReader.cpp)


  • InputDispatcher
  8) InputDispatcher.notifyKey (InputDispatcher.cpp)
     a) 生成KeyEvent (通過呼叫event.initialize)
     b) 生成KeyEntry
     c) 呼叫enqueueInboundEventLocked(newEntry),把KeyEntry加入到InputDispatcher類的mInboundQueue佇列中
     d) 根據需要喚醒InputDispatccherThread執行緒


  9) InputDispatcher.dispatchOnce (InputDispatcher.cpp)
  10) InputDispatcher.dispatchOnceInnerLocked (InputDispatcher.cpp)
      從mInboundQueue佇列中取出EventEntry


  11) InputDispatcher.dispatchKeyLocked (InputDispatcher.cpp)
      a) 從當前啟用視窗mFocusedWindowHandle中獲取InputWindowInfo
      b) 從InputWindowInfo中獲取inputChannel、frameLeft、frameTop並儲存在mCurrentInputTargets的top InputTarget成員中。後面InputDispatcher就會從mCurrentInputTargets中取出恰當的Activity視窗,然後把鍵盤事件分發給它
      c) 呼叫dispatchEventToCurrentInputTargetsLocked(currentTime, entry, false)


  12) InputDispatcher.dispatchEventToCurrentInputTargetsLocked (InputDispatcher.cpp)

// InputDispatcher.cpp
void InputDispatcher::dispatchEventToCurrentInputTargetsLocked(nsecs_t currentTime,
        EventEntry* eventEntry, bool resumeWithAppendedMotionSample) {

    LOG_ASSERT(eventEntry->dispatchInProgress); // should already have been set to true

    pokeUserActivityLocked(eventEntry);

    for (size_t i = 0; i < mCurrentInputTargets.size(); i++) {
        const InputTarget& inputTarget = mCurrentInputTargets.itemAt(i);

        ssize_t connectionIndex = getConnectionIndexLocked(inputTarget.inputChannel);
        if (connectionIndex >= 0) {

            // 獲取對應的Connection
            sp<Connection> connection = mConnectionsByReceiveFd.valueAt(connectionIndex);

            prepareDispatchCycleLocked(currentTime, connection, eventEntry, & inputTarget,
                    resumeWithAppendedMotionSample);
        } else {

        }
    }
}

  13) InputDispatcher.prepareDispatchCycleLocked (InputDispatcher.cpp)

  14) InputDispatcher.enqueueDispatchEntriesLocked (InputDispatcher.cpp)
      (1) InputDispatcher::enqueueDispatchEntryLocked (InputDispatcher.cpp)
        a) 生成DispatchEntry
        b) 把dispatchEntry加入connection->outboundQueue
         (connection->outboundQueue.enqueueAtTail(dispatchEntry))
      (2) InputDispatcher::startDispatchCycleLocked(以前佇列空才執行)

  15) InputDispatcher.startDispatchCycleLocked (InputDispatcher.cpp)
      (1) 從connection->outboundQueue中取出DispatchEntry
      (2) 對於EventEntry::TYPE_KEY,呼叫connection->inputPublisher.publishKeyEvent
          或 對於EventEntry::TYPE_MOTION,呼叫connection->inputPublisher.publishMotionEvent

      (3) InputPublisher.publishKeyEvent (InputTransport.cpp)
          它把key event存入ashmem buffer中,即mSharedMessage->key中,
          此ashmem buffer在InputDispatcher.registerInputChannel (connection->initialize)被建立。
     
      (4) 傳送dispath訊號(即寫傳送pipe)給Activity應用程式
          connection->inputPublisher.sendDispatchSignal-> InputChannel.sendSignal 

status_t InputPublisher::sendDispatchSignal() {
    mWasDispatched = true;
    return mChannel->sendSignal(INPUT_SIGNAL_DISPATCH);
}


      至此,已經向Activity應用程式接收pipe中寫入內容,則Activity應用程式的主執行緒就被喚醒了,開始處理此事件,即Activity應用程式上場了!

2.2 Client端 (Activity應用程式)

      當應用程式的主執行緒因為這個InputChannel中的讀管道被寫端喚醒時,NativeInputQueue的成員函式handleReceiveCallback就會被回撥,因此,接下來,應用程式的主執行緒就會被喚醒,然後執行NativeInputQueue的成員函式handleReceiveCallback。

   • NativeInputQueue
   1) NativeInputQueue::handleReceiveCallback (android_view_InputQueue.cpp)
     詳細見容見下面的註釋

int NativeInputQueue::handleReceiveCallback(int receiveFd, int events, void* data) {
    NativeInputQueue* q = static_cast<NativeInputQueue*>(data);
    JNIEnv* env = AndroidRuntime::getJNIEnv();

    sp<Connection> connection;
    InputEvent* inputEvent;
    jobject inputHandlerObjLocal;
    jlong finishedToken;
    { // acquire lock
        AutoMutex _l(q->mLock);

        // 根據receiveFd獲取 connectionIndex
        ssize_t connectionIndex = q->mConnectionsByReceiveFd.indexOfKey(receiveFd);
         ...
        // 根據connectionIndex獲取NativeInputQueue.Connection
        connection = q->mConnectionsByReceiveFd.valueAt(connectionIndex);
        ...
        // 從管道讀取資料並檢查是否與傳送方寫的一致
        status_t status = connection->inputConsumer.receiveDispatchSignal();
         ...
        // 獲取NativeInputQueue.Connection中的InputConsumer物件,它與Server端的InputPublisher對應
        // InputConsumer::consume (InputTransport.cpp), 負責把事件讀出來(MotionEvent或KeyEvent)並生成inputEventObj
        status = connection->inputConsumer.consume(& connection->inputEventFactory, & inputEvent);
        ...       

        connection->messageInProgress = true;
        connection->messageSeqNum += 1;

        finishedToken = generateFinishedToken(receiveFd, connection->id, connection->messageSeqNum);
 
        // 獲取inputHandlerObjLocal物件
        // 在NativeInputQueue.registerInputChannel中Java傳入,並儲存在Connection中
        inputHandlerObjLocal = env->NewLocalRef(connection->inputHandlerObjGlobal);
    } // release lock

    int32_t inputEventType = inputEvent->getType();

    jobject inputEventObj;
    jmethodID dispatchMethodId;
    switch (inputEventType) {
    case AINPUT_EVENT_TYPE_KEY:

        // 生成inputEventObj
        inputEventObj = android_view_KeyEvent_fromNative(env,
                static_cast<KeyEvent*>(inputEvent));
        
        // 以指定inputHandlerObjLocal呼叫InputQueue.dispatchKeyEvent來處理此事件 
        dispatchMethodId = gInputQueueClassInfo.dispatchKeyEvent;
        break;

    case AINPUT_EVENT_TYPE_MOTION:
        inputEventObj = android_view_MotionEvent_obtainAsCopy(env,
                static_cast<MotionEvent*>(inputEvent));
        dispatchMethodId = gInputQueueClassInfo.dispatchMotionEvent;
        break;

    default:
        assert(false); // InputConsumer should prevent this from ever happening
        inputEventObj = NULL;
    }

    if (! inputEventObj) {
        LOGW("channel '%s' ~ Failed to obtain DVM event object.",
                connection->getInputChannelName());
        env->DeleteLocalRef(inputHandlerObjLocal);
        q->finished(env, finishedToken, false, false);
        return 1;
    }
    
    // 通知Java層的InputQueue來處理這個事件
    env->CallStaticVoidMethod(gInputQueueClassInfo.clazz,
            dispatchMethodId, inputHandlerObjLocal, inputEventObj,
            jlong(finishedToken));

    if (env->ExceptionCheck()) {
        LOGE("An exception occurred while invoking the input handler for an event.");
        LOGE_EX(env);
        env->ExceptionClear();

        q->finished(env, finishedToken, false, true /*ignoreSpuriousFinish*/);
    }

    env->DeleteLocalRef(inputEventObj);
    env->DeleteLocalRef(inputHandlerObjLocal);
    return 1;
}


   • NativeInputQueue (下面以處理KeyEvent來講)
   2) InputQueue.dispatchKeyEvent (InputQueue.java)
  

    private static void dispatchKeyEvent(InputHandler inputHandler,
            KeyEvent event, long finishedToken) {
        FinishedCallback finishedCallback = FinishedCallback.obtain(finishedToken);
        // inputHandler是呼叫InputQueue.registerInputChannel時傳進來的
        // 在ViewRootImpl.setView中呼叫
        // InputQueue.registerInputChannel(mInputChannel, mInputHandler,
        //                        Looper.myQueue())
        inputHandler.handleKey(event, finishedCallback);
    }

         mInputHandler的定義如下:

    private final InputHandler mInputHandler = new InputHandler() {
        public void handleKey(KeyEvent event, InputQueue.FinishedCallback finishedCallback) {
            startInputEvent(finishedCallback);
            dispatchKey(event, true);
        }

        public void handleMotion(MotionEvent event, InputQueue.FinishedCallback finishedCallback) {
            startInputEvent(finishedCallback);
            dispatchMotion(event, true);
        }
    };


   3) mInputHandler.handleKey (ViewRootImpl.java)
   4) ViewRootImpl.dispatchKey (ViewRootImpl.java)
      把KeyEvent封裝成Message
   5) ViewRootImpl.enqueueInputEvent
      把Message轉換為InputEventMessage訊息,然後放於mPendingInputEvents連結串列尾。
      ViewRootImpl不直接處理這個事件,而是把它作為一個訊息(DISPATCH_KEY)放到訊息佇列中去處理,這個訊息最後由ViewRootImpl類的deliverKeyEvent成員函式來處理
   6) 在handleMessage中呼叫deliverKeyEvent

   7) ViewRootImpl.deliverKeyEvent (ViewRootImpl.java)

    private void deliverKeyEvent(KeyEvent event, boolean sendDone) {
        if (ViewDebug.DEBUG_LATENCY) {
            mInputEventDeliverTimeNanos = System.nanoTime();
        }

        if (mInputEventConsistencyVerifier != null) {
            mInputEventConsistencyVerifier.onKeyEvent(event, 0);
        }

        // If there is no view, then the event will not be handled.
        if (mView == null || !mAdded) {
            finishKeyEvent(event, sendDone, false);
            return;
        }

   
        // Perform predispatching before the IME.
        if (mView.dispatchKeyEventPreIme(event)) {
            finishKeyEvent(event, sendDone, true);
            return;
        }

        // InputMethodManager處理完這個鍵盤事件後,再回呼叫這裡的
        // mInputMethodCallback物件的finishedEvent成員函式來把鍵盤
        // 事件分發給當前啟用的Activity視窗處理。當然,在把這個鍵盤事件
        // 分發給InputMethodManager處理之前,ViewRoot也會先把這個鍵盤事
        // 件分發給當前啟用的Activity視窗的dispatchKeyEventPreIme成員函式處理。 

        // Dispatch to the IME before propagating down the view hierarchy.
        // The IME will eventually call back into handleFinishedEvent.
        if (mLastWasImTarget) {
            InputMethodManager imm = InputMethodManager.peekInstance();
            if (imm != null) {
                int seq = enqueuePendingEvent(event, sendDone);
                imm.dispatchKeyEvent(mView.getContext(), seq, event, mInputMethodCallback);
                return;
            }
        }

        // Not dispatching to IME, continue with post IME actions.
        deliverKeyEventPostIme(event, sendDone);
    }     


    8) InputMethodCallack.finishedEvent (ViewRootImpl.java)
    9) ViewRootImpl.dispatchFinishedEvent (ViewRootImpl.java)
       傳送FINISHED_EVENT到佇列
    10) ViewRootImpl.dispatchFinishedEvent (ViewRootImpl.java)
        如果InputMethodManager沒有處理這個鍵盤事件,那麼ViewRoot就會把
        這個鍵盤事件分發給當前啟用的Activity視窗來處理。
    11) ViewRootImpl.deliverKeyEventPostIme (ViewRootImpl.java)
    12) mView.dispatchKeyEvent(event) (ViewRootImpl.java)
        把事件分發給view hierarchy, mView為DecorView
    13) DecorView.dispatchKeyEvent (PhoneWindow.java)

          public boolean dispatchKeyEvent(KeyEvent event) {
            final int keyCode = event.getKeyCode();
            final int action = event.getAction();
            final boolean isDown = action == KeyEvent.ACTION_DOWN;

            if (!isDestroyed()) {
                // 返回當前應用程式的啟用的Activity視窗的Window.Callback介面,一般不為NULL
                // 因此,這個函式會呼叫Activity類的dispatchKeyEvent來處理這個鍵盤事件
                final Callback cb = getCallback();

                final boolean handled = cb != null && mFeatureId < 0 ? cb.dispatchKeyEvent(event)
                        : super.dispatchKeyEvent(event);
                if (handled) {
                    return true;
                }
            }

            return isDown ? PhoneWindow.this.onKeyDown(mFeatureId, event.getKeyCode(), event)
                    : PhoneWindow.this.onKeyUp(mFeatureId, event.getKeyCode(), event);
        }   

    14) Activity.dispatchKeyEvent (Activity.java)

  public boolean dispatchKeyEvent(KeyEvent event) {
        onUserInteraction();
        Window win = getWindow();
        if (win.superDispatchKeyEvent(event)) {
            return true;
        }
        View decor = mDecor;
        if (decor == null) decor = win.getDecorView();
        return event.dispatch(this, decor != null
                ? decor.getKeyDispatcherState() : null, this);
    }

    Activity不是直接處理這個鍵盤事件,而是通過KeyEvent的dispatch轉發一下。
    注意,KeyEvent的成中函式dispatch的第一個引數的型別是KeyEvent.Callback,而Activity實現了這個介面,因此,這裡可以傳this引用過去。

   15) KeyEvent.dispatch (KeyEvent.java)
      根據一個鍵是按下(ACTION_DOWN)、還是鬆開(ACTION_UP) 或者是一個相同的鍵被多次按下和鬆開(ACTION_MULTIPLE)等不同事件型別來分別呼叫(receiver為Activity)Activity的onKeyDown、onKeyLongPres、sonKeyUp和onKeyMultiple函數了。
      執行完此函式,然後再一層一層向上返回。