按键事件在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()函数。以上逻辑都比较简单了,不在详细分析。

你可能感兴趣的:(android)