我们在写应用的过程中,监听back键都是使用的onKeyDown的方法
/**
* Called when a key was pressed down and not handled by any of the views
* inside of the activity. So, for example, key presses while the cursor
* is inside a TextView will not trigger the event (unless it is a navigation
* to another object) because TextView handles its own key presses.
*
* <p>If the focused view didn't want this event, this method is called.
*
* <p>The default implementation takes care of {@link KeyEvent#KEYCODE_BACK}
* by calling {@link #onBackPressed()}, though the behavior varies based
* on the application compatibility mode: for
* {@link android.os.Build.VERSION_CODES#ECLAIR} or later applications,
* it will set up the dispatch to call {@link #onKeyUp} where the action
* will be performed; for earlier applications, it will perform the
* action immediately in on-down, as those versions of the platform
* behaved.
*
* <p>Other additional default key handling may be performed
* if configured with {@link #setDefaultKeyMode}.
*
* @return Return <code>true</code> to prevent this event from being propagated
* further, or <code>false</code> to indicate that you have not handled
* this event and it should continue to be propagated.
* @see #onKeyUp
* @see android.view.KeyEvent
*/
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK) {
if (getApplicationInfo().targetSdkVersion
>= Build.VERSION_CODES.ECLAIR) {
event.startTracking();
}else {
onBackPressed();
}
return true;
}
if (mDefaultKeyMode ==DEFAULT_KEYS_DISABLE) {
return false;
}else if (mDefaultKeyMode ==DEFAULT_KEYS_SHORTCUT) {
Window w = getWindow();
if (w.hasFeature(Window.FEATURE_OPTIONS_PANEL) &&
w.performPanelShortcut(Window.FEATURE_OPTIONS_PANEL, keyCode, event,
Menu.FLAG_ALWAYS_PERFORM_CLOSE)) {
return true;
}
return false;
}else if (keyCode == KeyEvent.KEYCODE_TAB) {
// Don't consume TAB here since it's used for navigation. Arrow keys
// aren't considered "typing keys" so they already won't get consumed.
return false;
}else {
// Common code for DEFAULT_KEYS_DIALER & DEFAULT_KEYS_SEARCH_*
boolean clearSpannable =false;
boolean handled;
if ((event.getRepeatCount() !=0) || event.isSystem()) {
clearSpannable =true;
handled =false;
}else {
handled = TextKeyListener.getInstance().onKeyDown(
null, mDefaultKeySsb, keyCode, event);
if (handled &&mDefaultKeySsb.length() >0) {
// something useable has been typed - dispatch it now.
final String str =mDefaultKeySsb.toString();
clearSpannable =true;
switch (mDefaultKeyMode) {
case DEFAULT_KEYS_DIALER:
Intent intent =new Intent(Intent.ACTION_DIAL, Uri.parse("tel:" + str));
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
break;
case DEFAULT_KEYS_SEARCH_LOCAL:
startSearch(str, false, null, false);
break;
case DEFAULT_KEYS_SEARCH_GLOBAL:
startSearch(str, false, null, true);
break;
}
}
}
if (clearSpannable) {
mDefaultKeySsb.clear();
mDefaultKeySsb.clearSpans();
Selection.setSelection(mDefaultKeySsb,0);
}
return handled;
}
}
其中键值都是从KeyEvent传过来的,那么back这个键值是从哪过来的呢
public class KeyEvent extends InputEvent implements Parcelable
KeyEvent继承了InputEvent,在KeyEvent类中有一个Callback接口
public interface Callback {
/**
* Called when a key down event has occurred. If you return true,
* you can first call {@link KeyEvent#startTracking()
* KeyEvent.startTracking()} to have the framework track the event
* through its {@link #onKeyUp(int, KeyEvent)} and also call your
* {@link #onKeyLongPress(int, KeyEvent)} if it occurs.
*
* @param keyCode The value in event.getKeyCode().
* @param event Description of the key event.
*
* @return If you handled the event, return true. If you want to allow
* the event to be handled by the next receiver, return false.
*/
boolean onKeyDown(int keyCode, KeyEvent event);
在这个接口中我们看到了OnKeyDown的方法,从名字就可以看出这是一个回调方法
![](https://img.haomeiwen.com/i16249515/d2de1a2c02a76d6c.png)
从图中我们可以看到大部分的方法都是调用了super.OnKeyDown,但是在KeyEvent类中又调用了这个方法的痕迹
/**
* Deliver this key event to a {@link Callback} interface. If this is
* an ACTION_MULTIPLE event and it is not handled, then an attempt will
* be made to deliver a single normal event.
*
* @param receiver The Callback that will be given the event.
* @param state State information retained across events.
* @param target The target of the dispatch, for use in tracking.
*
* @return The return value from the Callback method that was called.
*/
public final boolean dispatch(Callback receiver, DispatcherState state,
Object target) {
switch (mAction) {
case ACTION_DOWN: {
mFlags &= ~FLAG_START_TRACKING;
if (DEBUG) Log.v(TAG, "Key down to " + target + " in " + state
+ ": " + this);
boolean res = receiver.onKeyDown(mKeyCode, this);
if (state != null) {
if (res && mRepeatCount == 0 && (mFlags&FLAG_START_TRACKING) != 0) {
if (DEBUG) Log.v(TAG, " Start tracking!");
state.startTracking(this, target);
} else if (isLongPress() && state.isTracking(this)) {
try {
if (receiver.onKeyLongPress(mKeyCode, this)) {
if (DEBUG) Log.v(TAG, " Clear from long press!");
state.performedLongPress(this);
res = true;
}
} catch (AbstractMethodError e) {
}
}
}
return res;
}
case ACTION_UP:
if (DEBUG) Log.v(TAG, "Key up to " + target + " in " + state
+ ": " + this);
if (state != null) {
state.handleUpEvent(this);
}
return receiver.onKeyUp(mKeyCode, this);
case ACTION_MULTIPLE:
final int count = mRepeatCount;
final int code = mKeyCode;
if (receiver.onKeyMultiple(code, count, this)) {
return true;
}
if (code != KeyEvent.KEYCODE_UNKNOWN) {
mAction = ACTION_DOWN;
mRepeatCount = 0;
boolean handled = receiver.onKeyDown(code, this);
if (handled) {
mAction = ACTION_UP;
receiver.onKeyUp(code, this);
}
mAction = ACTION_MULTIPLE;
mRepeatCount = count;
return handled;
}
return false;
}
return false;
}
![](https://img.haomeiwen.com/i16249515/c5ce82f2fc9f33d6.png)
我们在往上倒,发现在Activity中调用了此方法
/**
* Called to process key events. You can override this to intercept all
* key events before they are dispatched to the window. Be sure to call
* this implementation for key events that should be handled normally.
*
* @param event The key event.
*
* @return boolean Return true if this event was consumed.
*/
public boolean dispatchKeyEvent(KeyEvent event) {
onUserInteraction();
// Let action bars open menus in response to the menu key prioritized over
// the window handling it
final int keyCode = event.getKeyCode();
if (keyCode == KeyEvent.KEYCODE_MENU &&
mActionBar != null && mActionBar.onMenuKeyEvent(event)) {
return true;
}
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);
}
看到这个方法咋觉得那么的熟悉呢,从注释上看,这个方法是处理所有key event事件的
![](https://img.haomeiwen.com/i16249515/c3f7cc10a5985456.png)
在网上倒发现在DecorView中有调用了这个方法,DecorView是activity的根view
@Override
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 ((mWindow.mPanelChordingKey > 0) && (mWindow.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 ((mWindow.mPreparedPanel != null) && mWindow.mPreparedPanel.isOpen) {
if (mWindow.performPanelShortcut(mWindow.mPreparedPanel, keyCode, event, 0)) {
return true;
}
}
}
if (!mWindow.isDestroyed()) {
final Window.Callback cb = mWindow.getCallback();
final boolean handled = cb != null && mFeatureId < 0 ? cb.dispatchKeyEvent(event)
: super.dispatchKeyEvent(event);
if (handled) {
return true;
}
}
return isDown ? mWindow.onKeyDown(mFeatureId, event.getKeyCode(), event)
: mWindow.onKeyUp(mFeatureId, event.getKeyCode(), event);
}
这还是一个被重写的方法
我们往上倒
public class DecorView extends FrameLayout implements RootViewSurfaceTaker, WindowCallbacks
public class FrameLayout extends ViewGroup {
public abstract class ViewGroup extends View implements ViewParent, ViewManager
public class View implements Drawable.Callback, KeyEvent.Callback, AccessibilityEventSource {
在view 与viewGroup中都有 dispatchKeyEvent方法
view中的dispatchKeyEvent
public boolean dispatchKeyEvent(KeyEvent event) {
if (mInputEventConsistencyVerifier != null) {
mInputEventConsistencyVerifier.onKeyEvent(event, 0);
}
// Give any attached key listener a first crack at the event.
//noinspection SimplifiableIfStatement
ListenerInfo li = mListenerInfo;
if (li != null && li.mOnKeyListener != null && (mViewFlags & ENABLED_MASK) == ENABLED
&& li.mOnKeyListener.onKey(this, event.getKeyCode(), event)) {
return true;
}
if (event.dispatch(this, mAttachInfo != null
? mAttachInfo.mKeyDispatchState : null, this)) {
return true;
}
if (mInputEventConsistencyVerifier != null) {
mInputEventConsistencyVerifier.onUnhandledEvent(event, 0);
}
return false;
}
ViewGroup中的dispatchKeyEvent
@Override
public boolean dispatchKeyEvent(KeyEvent event) {
if (mInputEventConsistencyVerifier != null) {
mInputEventConsistencyVerifier.onKeyEvent(event, 1);
}
if ((mPrivateFlags & (PFLAG_FOCUSED | PFLAG_HAS_BOUNDS))
== (PFLAG_FOCUSED | PFLAG_HAS_BOUNDS)) {
if (super.dispatchKeyEvent(event)) {
return true;
}
} else if (mFocused != null && (mFocused.mPrivateFlags & PFLAG_HAS_BOUNDS)
== PFLAG_HAS_BOUNDS) {
if (mFocused.dispatchKeyEvent(event)) {
return true;
}
}
if (mInputEventConsistencyVerifier != null) {
mInputEventConsistencyVerifier.onUnhandledEvent(event, 1);
}
return false;
}
有点偏了,那么我们接着看看谁调用了DecorView的dispatchKeyEvent方法吧
![](https://img.haomeiwen.com/i16249515/91d81f1254e0a149.png)
看到了PhoneWindow这个类,大家都是DecorView的外面都有一层PhoneWindow,那么肯定是这个类啦
进入到PhoneWindow中全局搜索没有搜索到dispatchKeyEvent方法调用的地,难道不在这里面?
![](https://img.haomeiwen.com/i16249515/ac41537b08796f08.png)
按照之前方法,我们看到在ViewRootImpl类中有调用此方法的地方,在ViewGroup中也有,暂时先忽略
ViewRootImpl->processKeyEvent
private int processKeyEvent(QueuedInputEvent q) {
final KeyEvent event = (KeyEvent)q.mEvent;
if (mUnhandledKeyManager.preViewDispatch(event)) {
return FINISH_HANDLED;
}
// Deliver the key to the view hierarchy.
//再次调用了方法
if (mView.dispatchKeyEvent(event)) {
return FINISH_HANDLED;
}
if (shouldDropInputEvent(q)) {
return FINISH_NOT_HANDLED;
}
// This dispatch is for windows that don't have a Window.Callback. Otherwise,
// the Window.Callback usually will have already called this (see
// DecorView.superDispatchKeyEvent) leaving this call a no-op.
if (mUnhandledKeyManager.dispatch(mView, event)) {
return FINISH_HANDLED;
}
int groupNavigationDirection = 0;
if (event.getAction() == KeyEvent.ACTION_DOWN
&& event.getKeyCode() == KeyEvent.KEYCODE_TAB) {
if (KeyEvent.metaStateHasModifiers(event.getMetaState(), KeyEvent.META_META_ON)) {
groupNavigationDirection = View.FOCUS_FORWARD;
} else if (KeyEvent.metaStateHasModifiers(event.getMetaState(),
KeyEvent.META_META_ON | KeyEvent.META_SHIFT_ON)) {
groupNavigationDirection = View.FOCUS_BACKWARD;
}
}
// If a modifier is held, try to interpret the key as a shortcut.
if (event.getAction() == KeyEvent.ACTION_DOWN
&& !KeyEvent.metaStateHasNoModifiers(event.getMetaState())
&& event.getRepeatCount() == 0
&& !KeyEvent.isModifierKey(event.getKeyCode())
&& groupNavigationDirection == 0) {
if (mView.dispatchKeyShortcutEvent(event)) {
return FINISH_HANDLED;
}
if (shouldDropInputEvent(q)) {
return FINISH_NOT_HANDLED;
}
}
// Apply the fallback event policy.
if (mFallbackEventHandler.dispatchKeyEvent(event)) {
return FINISH_HANDLED;
}
if (shouldDropInputEvent(q)) {
return FINISH_NOT_HANDLED;
}
// Handle automatic focus changes.
if (event.getAction() == KeyEvent.ACTION_DOWN) {
if (groupNavigationDirection != 0) {
if (performKeyboardGroupNavigation(groupNavigationDirection)) {
return FINISH_HANDLED;
}
} else {
if (performFocusNavigation(event)) {
return FINISH_HANDLED;
}
}
}
return FORWARD;
}
我再一次往上倒
ViewRootImpl->ViewPostImeInputStage->onProcess
ViewRootImpl->deliverapply->apply->onProcess
ViewRootImpl->deliverInputEvent->deliver
ViewRootImpl->doProcessInputEvents->deliverInputEvent
enqueueInputEvent->doProcessInputEvents
![](https://img.haomeiwen.com/i16249515/a55c2c99ea2f3a76.png)
追到这里我好到有个handle,这个handle之前好像在别的帖子中见到过,应该就是这里
public void dispatchInputEvent(InputEvent event, InputEventReceiver receiver) {
SomeArgs args = SomeArgs.obtain();
args.arg1 = event;
args.arg2 = receiver;
Message msg = mHandler.obtainMessage(MSG_DISPATCH_INPUT_EVENT, args);
msg.setAsynchronous(true);
mHandler.sendMessage(msg);
}
调用这个方法发送的handle
public void dispatchInputEvent(InputEvent event)从这android studio就网上倒不上去了
我们回过头来,看看PhoneWindow中有没有掉用到这个方法
![](https://img.haomeiwen.com/i16249515/8939cd8f6e807e3c.png)
哈哈,果然有但是还是往上追不了,我们先看看PhoneWindow继承了谁吧
public class PhoneWindow extends Window implements MenuBuilder.Callback {
继承了Window,在Window中有这个方法
@Override
public void injectInputEvent(InputEvent event) {
getViewRootImpl().dispatchInputEvent(event);
}
追到这里追不下去了。。。
记得刚才在ViewRootImpl的enqueueInputEvent方法有好几处调用它的地方,从这从新开始
final class WindowInputEventReceiver extends InputEventReceiver {
public WindowInputEventReceiver(InputChannel inputChannel, Looper looper) {
super(inputChannel, looper);
}
@Override
public void onInputEvent(InputEvent event, int displayId) {
enqueueInputEvent(event, this, 0, true);
}
@Override
public void onBatchedInputEventPending() {
if (mUnbufferedInputDispatch) {
super.onBatchedInputEventPending();
} else {
scheduleConsumeBatchedInput();
}
}
@Override
public void dispose() {
unscheduleConsumeBatchedInput();
super.dispose();
}
}
WindowInputEventReceiver 这个类只有在ViewRootImpl的setView方法中有调用的地方
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
synchronized (this) {
...
if (mInputChannel != null) {
if (mInputQueueCallback != null) {
mInputQueue = new InputQueue();
mInputQueueCallback.onInputQueueCreated(mInputQueue);
}
mInputEventReceiver = new WindowInputEventReceiver(mInputChannel,
Looper.myLooper());
}
...
}
到目前为止算是java层算是追到头了,暂时总结一下
方法依次执行的顺序是
ViewRootImpl.setView->
ViewRootImpl.WindowInputEventReceiver.onInputEvent->
ViewRootImpl.enqueueInputEvent->
ViewRootImpl.doProcessInputEvents->
ViewRootImpl.deliverInputEvent->
ViewRootImpl.InputStage.deliver->
ViewRootImpl.apply->
ViewRootImpl.ViewPostImeInputStage.onProcess->
ViewRootImpl.processKeyEvent->
DecorView.dispatchKeyEvent->
ViewGroup.dispatchKeyEvent->
view.dispatchKeyEvent->
Activity.dispatchKeyEvent->
KeyEvent.dispatch->
Activity.OnKeyDown
网友评论