底层的事件传递不管,只研究在安卓代码里的传递。上文注册了InputChannel,里面有传入一个inputHandler作为参数。InputHandler就是一个接口,接口里就定义了handleKey和handleMotion两个方法。方法体是在ViewRootImpl里定义的,代码如下:
源码路径:frameworks\base\core\java\android\view\ViewRootImpl.java源码中国链接:http://www.oschina.net/code/explore/android-2.2-froyo/android/view/ViewRoot.java
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);
}
};
当有事件发生时,上面两个函数的其中一个被调用。
我们来看看dispatchKey这个函数
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.dispatchKeyEventPreIme(event)函数,再判断输入法是否打开,如果有的话会先将事件传入到输入法里,否则往下调用deliverKeyEventPostIme函数
这里的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函数。具体在
android下当键盘弹出时拦截Back事件
这篇文章里有讲述。
最后来看看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);
}
这里主要做了几件事情:
1、处理系统快捷键
2、调用View中Callback对象的dispatchKey Event(),即调用Activity的dispatchKeyEvent()
2、如果Activity没有消耗该消息,则调用PhoneWindow的OnKeyEvent()对消息做最后的处理
这里就将事件通知到了Activity了。再往下就是Activity的dispatchKeyEvent和onKeyDown函数被调用。