按鍵事件在java framework中的流程

按鍵事件在activity中的流程按鍵事件在native和jni中的流程兩篇文章主要探討了事件在activity中的處理流程和事件在native層的處理流程。本文則主要探討事件如何進入activity,以及如果activity未處理事件時,事件在framework中的處理。

事件如何進入activity
前面的文章已經講到了事件經過native和jni的處理之後,最終通過InputChannel進入到了ViewRootImpl。這個ViewRootImpl實現了ViewParent接口,是任何一個Window內的view層級的最頂級ViewParent。在ViewRootImpl中有一個WindowInputEventReceiver的內部類,它繼承於InputEventReceiver(可以認爲是InputChannel的回調,事件會進入其onInputEvent()函數中)。在WindowInputEventReceiver中,按鍵事件最終會送到ViewRootImpl的deliverKeyEvent()中(touch、trackball等事件也都有類似方法)。

    private void deliverKeyEvent(QueuedInputEvent q) { 
        final KeyEvent event = (KeyEvent)q.mEvent;
        if (mInputEventConsistencyVerifier != null) {
            mInputEventConsistencyVerifier.onKeyEvent(event, 0);
        }

        if (mView != null && mAdded && (q.mFlags & QueuedInputEvent.FLAG_DELIVER_POST_IME) == 0) { 
            if (LOCAL_LOGV) Log.v(TAG, "Dispatching key " + event + " to " + mView);

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

            // Dispatch to the IME before propagating down the view hierarchy.
            // The IME will eventually call back into handleImeFinishedEvent.
            if (mLastWasImTarget) {
                InputMethodManager imm = InputMethodManager.peekInstance();
                if (imm != null) {
                    final int seq = event.getSequenceNumber();
                    if (DEBUG_IMF) Log.v(TAG, "Sending key event to IME: seq="
                            + seq + " event=" + event);
                    imm.dispatchKeyEvent(mView.getContext(), seq, event, mInputMethodCallback);
                    return;
                }
            }
        }

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

函數中的mView實際上就是PhoneWindow中的DecorView。在deliverKeyEventPostIme()函數中,會先後調用mView的dispatchKeyEvent()和dispatchKeyShortcutEvent()方法,如果事件還是未被處理,並且按鍵是方向鍵時,則會做尋找焦點的邏輯。所以,綜合起來,大概邏輯就是先後調用了DecorView的dispatchKeyEventPreIme()、dispatchKeyEvent()、dispatchKeyShortcutEvent(),最後是找焦點。dispatchKeyEventPreIme()函數,在TextView中會有一些邏輯,其它地方基本都直接返回false。
注意,中間會有和IMM的交互。而IMM會將事件通過InputMethodSession接口,將事件送入InputMethodService的onKeyDown()和onKeyUp()函數,處理一些輸入文件選中的一些邏輯,不再詳敘。

dispatchKeyEvent()函數的代碼如下:

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

            if (isDown && (event.getRepeatCount() == 0)) {
                // First handle chording of panel key: if a panel key is held
                // but not released, try to execute a shortcut in it.
                if ((mPanelChordingKey > 0) && (mPanelChordingKey != keyCode)) {
                    boolean handled = dispatchKeyShortcutEvent(event);
                    if (handled) {
                        return true;
                    }
                }

                // If a panel is open, perform a shortcut on it without the
                // chorded panel key
                if ((mPreparedPanel != null) && mPreparedPanel.isOpen) {
                    if (performPanelShortcut(mPreparedPanel, keyCode, event, 0)) {
                        return true;
                    }
                }
            }

            if (!isDestroyed()) {
                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);
        }

核心邏輯是將事件送給了Window.callback.dispatchKeyEvent(),然後會進入PhoneWindow的onKeyDown()和onKeyUp()中。而activity正實現了Window.Callback。也就是說事件會先進入activity的dispatchKeyEvent()中,這也是我們在按鍵事件在activity中的流程一文中所說的事件在activity中的起點。


事件在PhoneWindow中的處理
PhoneWindow的onKeyDown()和onKeyUp()函數都很簡單,主要會處理vol、menu、search相關的按鍵。vol將傳遞給AudioManager,顯示音量調節。menu則主要和ActionBar交互,顯示/隱藏菜單之類的。search則會通過Window.Callback進入activity,並最終調用SearchManager.startSearch()函數。以上邏輯都比較簡單了,不在詳細分析。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章