底层的事件传递不管,只研究在安卓代码里的传递。上文注册了InputChannel,里面有传入一个inputHandler作为参数。InputHandler就是一个接口,接口里就定义了handleKey和handleMotion两个方法。方法体是在ViewRootImpl里定义的,代码如下:
源码路径:frameworks\base\core\java\android\view\ViewRootImpl.javaprivate 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); } };当有事件发生时,上面两个函数的其中一个被调用。
private void dispatchKey(KeyEvent event, boolean sendDone) { //noinspection ConstantConditions if (false && event.getAction() == KeyEvent.ACTION_DOWN) { if (event.getKeyCode() == KeyEvent.KEYCODE_CAMERA) { if (DBG) Log.d("keydisp", "==================================================="); if (DBG) Log.d("keydisp", "Focused view Hierarchy is:"); debug(); if (DBG) Log.d("keydisp", "==================================================="); } } Message msg = obtainMessage(DISPATCH_KEY); msg.obj = event; msg.arg1 = sendDone ? 1 : 0; if (LOCAL_LOGV) Log.v( TAG, "sending key " + event + " to " + mView); sendMessageAtTime(msg, event.getEventTime()); }
主要是包装并发送了一个关于这个事件的消息。由于ViewRootImpl是一个Handler,那么sendMessageAtTime其实是Handler的一个方法。根据我们的经验,接下来就是
被调用了。这个方法处理了很多种消息,此处只截出跟DISPATCH_KEY这个消息有关的部分:
public void handleMessage(Message msg) { switch (msg.what) { case DISPATCH_KEY: deliverKeyEvent((KeyEvent)msg.obj, msg.arg1 != 0); break; } }再来看看deliverKeyEvent
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; } if (LOCAL_LOGV) Log.v(TAG, "Dispatching key " + event + " to " + mView); // Perform predispatching before the IME. if (mView.dispatchKeyEventPreIme(event)) { finishKeyEvent(event, sendDone, true); return; } // 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); 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(event, sendDone); }
这里的mView,就是通过setView函数传递进来的参数view, 也即窗体的最外层View。对于应用窗口涞说实际上就是PhoneWindow.DecorView。DecorView没有重写dispatchKeyEventPreIme函数,实际上调用的是ViewGroup的:
ViewGroup
@Override public boolean dispatchKeyEventPreIme(KeyEvent event) { if ((mPrivateFlags & (FOCUSED | HAS_BOUNDS)) == (FOCUSED | HAS_BOUNDS)) { return super.dispatchKeyEventPreIme(event); } else if (mFocused != null && (mFocused.mPrivateFlags & HAS_BOUNDS) == HAS_BOUNDS) { return mFocused.dispatchKeyEventPreIme(event); } return false; }
如果我们需要在键盘弹出时拦截一些事件,比如back事件。就需要重写窗体的根布局并重写dispatchKeyEventPreIme函数。具体在
这篇文章里有讲述。
最后来看看deliverKeyEventPostIme做了什么事情(发现这个函数好长,我就只贴重点的吧):
if (mView.dispatchKeyEvent(event)) { finishKeyEvent(event, sendDone, true); return; }这里的mView对于应用窗口涞说就是PhoneWindow.DecorView。我们来看看DecorView中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); }这里主要做了几件事情:
2、如果Activity没有消耗该消息,则调用PhoneWindow的OnKeyEvent()对消息做最后的处理
这里就将事件通知到了Activity了。再往下就是Activity的dispatchKeyEvent和onKeyDown函数被调用。