android开发浅谈之KeyEvent事件处理

一个key事件的调用过程

一个偶然的机会,有一个报错的问题,日志如下:

AndroidRuntime: 	at android.view.View.performClick(View.java:7259)
AndroidRuntime: 	at android.view.View.performClickInternal(View.java:7236)
AndroidRuntime: 	at android.view.View.onKeyUp(View.java:14697)
AndroidRuntime: 	at android.view.KeyEvent.dispatch(KeyEvent.java:2902)
AndroidRuntime: 	at android.view.View.dispatchKeyEvent(View.java:13890)
AndroidRuntime: 	at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1917)
AndroidRuntime: 	at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1922)
AndroidRuntime: 	at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1922)
AndroidRuntime: 	at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1922)
AndroidRuntime: 	at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1922)
AndroidRuntime: 	at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1922)
AndroidRuntime: 	at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1922)
AndroidRuntime: 	at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1922)
AndroidRuntime: 	at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1922)
AndroidRuntime: 	at com.android.internal.policy.DecorView.superDispatchKeyEvent(DecorView.java:473)
AndroidRuntime: 	at com.android.internal.policy.PhoneWindow.superDispatchKeyEvent(PhoneWindow.java:1871)
AndroidRuntime: 	at android.app.Activity.dispatchKeyEvent(Activity.java:4093)
AndroidRuntime: 	at androidx.core.app.ComponentActivity.superDispatchKeyEvent(ComponentActivity.java:122)
AndroidRuntime: 	at androidx.core.view.KeyEventDispatcher.dispatchKeyEvent(KeyEventDispatcher.java:84)
AndroidRuntime: 	at androidx.core.app.ComponentActivity.dispatchKeyEvent(ComponentActivity.java:140)
AndroidRuntime: 	at androidx.appcompat.app.AppCompatActivity.dispatchKeyEvent(AppCompatActivity.java:569)
AndroidRuntime: 	at androidx.appcompat.view.WindowCallbackWrapper.dispatchKeyEvent(WindowCallbackWrapper.java:59)
AndroidRuntime: 	at androidx.appcompat.app.AppCompatDelegateImpl$AppCompatWindowCallback.dispatchKeyEvent(AppCompatDelegateImpl.java:3054)
AndroidRuntime: 	at com.android.internal.policy.DecorView.dispatchKeyEvent(DecorView.java:385)
AndroidRuntime: 	at android.view.ViewRootImpl$ViewPostImeInputStage.processKeyEvent(ViewRootImpl.java:5698)
AndroidRuntime: 	at android.view.ViewRootImpl$ViewPostImeInputStage.onProcess(ViewRootImpl.java:5562)
AndroidRuntime: 	at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:5069)
AndroidRuntime: 	at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:5122)
AndroidRuntime: 	at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:5088)
AndroidRuntime: 	at android.view.ViewRootImpl$AsyncInputStage.forward(ViewRootImpl.java:5228)
AndroidRuntime: 	at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:5096)
AndroidRuntime: 	at android.view.ViewRootImpl$AsyncInputStage.apply(ViewRootImpl.java:5285)
AndroidRuntime: 	at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:5069)
AndroidRuntime: 	at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:5122)
AndroidRuntime: 	at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:5088)
AndroidRuntime: 	at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:5096)
AndroidRuntime: 	at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:5069)
AndroidRuntime: 	at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:5122)
AndroidRuntime: 	at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:5088)
AndroidRuntime: 	at android.view.ViewRootImpl$AsyncInputStage.forward(ViewRootImpl.java:5261)
AndroidRuntime: 	at android.view.ViewRootImpl$ImeInputStage.onFinishedInputEvent(ViewRootImpl.java:5422)
AndroidRuntime: 	at android.view.inputmethod.InputMethodManager$PendingEvent.run(InputMethodManager.java:3067)
AndroidRuntime: 	at android.view.inputmethod.InputMethodManager.invokeFinishedInputEventCallback(InputMethodManager.java:2610)
AndroidRuntime: 	at android.view.inputmethod.InputMethodManager.finishedInputEvent(InputMethodManager.java:2601)
AndroidRuntime: 	at android.view.inputmethod.InputMethodManager$ImeInputEventSender.onInputEventFinished(InputMethodManager.java:3044)
AndroidRuntime: 	at android.view.InputEventSender.dispatchInputEventFinished(InputEventSender.java:143)
AndroidRuntime: 	at android.os.MessageQueue.nativePollOnce(Native Method)
AndroidRuntime: 	at android.os.MessageQueue.next(MessageQueue.java:336)
AndroidRuntime: 	at android.os.Looper.loop(Looper.java:174)
AndroidRuntime: 	at android.app.ActivityThread.main(ActivityThread.java:7618)
AndroidRuntime: 	at java.lang.reflect.Method.invoke(Native Method)
AndroidRuntime: 	at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492)
AndroidRuntime: 	at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:935)

一看这个报错日志,乖乖,这不就是一个key事件的完整调用过程吗。

下面我们重点关注一下几个关键的方法:

关键方法的分析

ViewRootImpl$ViewPostImeInputStage.onProcess

ViewRootImpl$ViewPostImeInputStage.onProcess方法:

protected int onProcess(QueuedInputEvent q) {
    if (q.mEvent instanceof KeyEvent) {
        return processKeyEvent(q);//如果是keyEvent事件,就调用processKeyEvent方法
    } else {
        final int source = q.mEvent.getSource();
        if ((source & InputDevice.SOURCE_CLASS_POINTER) != 0) {
            return processPointerEvent(q);
        } else if ((source & InputDevice.SOURCE_CLASS_TRACKBALL) != 0) {
            return processTrackballEvent(q);
        } else {
            return processGenericMotionEvent(q);
        }
    }
}

ViewRootImpl$ViewPostImeInputStage.processKeyEvent

ViewRootImpl$ViewPostImeInputStage.processKeyEvent方法:

private int processKeyEvent(QueuedInputEvent q) {
    final KeyEvent event = (KeyEvent)q.mEvent;

    if (mUnhandledKeyManager.preViewDispatch(event)) {
        return FINISH_HANDLED;
    }

    Slog.d(mTag, "processKeyEvent , event = " + event + ", mView = " + mView + ", context = " + mContext + ", package = " + mContext.getPackageName());

    // Deliver the key to the view hierarchy.
    if (mView.dispatchKeyEvent(event)) {//调用mView.dispatchKeyEvent方法
        return FINISH_HANDLED;
    }
......
}

DecorView.dispatchKeyEvent

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 ((mWindow.mPanelChordingKey > 0) && (mWindow.mPanelChordingKey != keyCode)) {
           boolean handled = dispatchKeyShortcutEvent(event);//调用dispatchKeyShortcutEvent方法
           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()) {

       Log.i(mLogTag, "dispatchKeyEvent , event = " + event + ", cb = " + mWindow.getCallback() + ", mFeatureId = " + mFeatureId + ", context = " + mContext
           + ", package = " + mContext.getPackageName());

       final Window.Callback cb = mWindow.getCallback();
       final boolean handled = cb != null && mFeatureId < 0 ? cb.dispatchKeyEvent(event)
               : super.dispatchKeyEvent(event);//如果mWindow有Callback 接口,就会调用Callback的dispatchKeyEvent方法,或者调用super.dispatchKeyEvent

       Log.i(mLogTag, "dispatchKeyEvent , handled = " + handled);  
       
       if (handled) {
           return true;
       }
   }

   return isDown ? mWindow.onKeyDown(mFeatureId, event.getKeyCode(), event)
           : mWindow.onKeyUp(mFeatureId, event.getKeyCode(), event);//调用mWindow的onKeyDown或onKeyUp方法
}

Activity.dispatchKeyEvent

Activity.dispatchKeyEvent方法:

/**
 * 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)) {//调用win.superDispatchKeyEvent方法
        return true;
    }
    View decor = mDecor;
    if (decor == null) decor = win.getDecorView();
    return event.dispatch(this, decor != null
            ? decor.getKeyDispatcherState() : null, this);//调用event.dispatch方法
}

PhoneWindow.superDispatchKeyEvent

PhoneWindow.superDispatchKeyEvent方法:

public boolean superDispatchKeyEvent(KeyEvent event) {
    return mDecor.superDispatchKeyEvent(event);
}

DecorView.superDispatchKeyEvent

DecorView.superDispatchKeyEvent方法:

public boolean superDispatchKeyEvent(KeyEvent event) {
    // Give priority to closing action modes if applicable.
    if (event.getKeyCode() == KeyEvent.KEYCODE_BACK) {//处理back key
        final int action = event.getAction();
        // Back cancels action modes first.
        if (mPrimaryActionMode != null) {
            if (action == KeyEvent.ACTION_UP) {
                mPrimaryActionMode.finish();
            }
            return true;
        }
    }

    if (super.dispatchKeyEvent(event)) {//调用super.dispatchKeyEvent
        return true;
    }

    return (getViewRootImpl() != null) && getViewRootImpl().dispatchUnhandledKeyEvent(event);//调用getViewRootImpl().dispatchUnhandledKeyEvent
}

ViewGroup.dispatchKeyEvent

ViewGroup.dispatchKeyEvent方法:

// The view contained within this ViewGroup that has or contains focus.
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
private View mFocused;
......

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)) {//调用mFocused.dispatchKeyEvent
            return true;
        }
    }

    if (mInputEventConsistencyVerifier != null) {
        mInputEventConsistencyVerifier.onUnhandledEvent(event, 1);
    }
    return false;
}

View.dispatchKeyEvent

View.dispatchKeyEvent方法:

/**
 * Dispatch a key event to the next view on the focus path. This path runs
 * from the top of the view tree down to the currently focused view. If this
 * view has focus, it will dispatch to itself. Otherwise it will dispatch
 * the next node down the focus path. This method also fires any key
 * listeners.
 *
 * @param event The key event to be dispatched.
 * @return True if the event was handled, false otherwise.
 */
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)) {//对于view,调用li.mOnKeyListener.onKey方法处理key事件
        return true;
    }

    if (event.dispatch(this, mAttachInfo != null
            ? mAttachInfo.mKeyDispatchState : null, this)) {//调用event.dispatch方法
        return true;
    }

    if (mInputEventConsistencyVerifier != null) {
        mInputEventConsistencyVerifier.onUnhandledEvent(event, 0);//调用mInputEventConsistencyVerifier.onUnhandledEvent方法
    }
    return false;
}

KeyEvent.dispatch

KeyEvent.dispatch方法:

static final boolean DEBUG = (!android.os.Build.IS_USER);//false;
......

/**
 * 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);//调用receiver.onKeyDown
            if (state != null) {
                if (res && mRepeatCount == 0 && (mFlags&FLAG_START_TRACKING) != 0) {
                    if (DEBUG) Log.v(TAG, "  Start tracking!");
                    state.startTracking(this, target);//调用state.startTracking
                } else if (isLongPress() && state.isTracking(this)) {
                    try {
                        if (receiver.onKeyLongPress(mKeyCode, this)) {//调用receiver.onKeyLongPress方法
                            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);//调用state.handleUpEvent方法
            }
            return receiver.onKeyUp(mKeyCode, this);//调用receiver.onKeyUp方法
        case ACTION_MULTIPLE:
            final int count = mRepeatCount;
            final int code = mKeyCode;
            if (receiver.onKeyMultiple(code, count, this)) {//调用receiver.onKeyMultiple方法
                return true;
            }
            if (code != KeyEvent.KEYCODE_UNKNOWN) {
                mAction = ACTION_DOWN;
                mRepeatCount = 0;
                boolean handled = receiver.onKeyDown(code, this);//调用receiver.onKeyDown
                if (handled) {
                    mAction = ACTION_UP;
                    receiver.onKeyUp(code, this);//调用receiver.onKeyUp方法
                }
                mAction = ACTION_MULTIPLE;
                mRepeatCount = count;
                return handled;
            }
            return false;
    }
    return false;
}

这个日志的关键标记位:DEBUG ,建议userdebug版本打开,方便定位KeyEvent的问题。

View.onKeyUp

View.onKeyUp方法:

/**
 * Default implementation of {@link KeyEvent.Callback#onKeyUp(int, KeyEvent)
 * KeyEvent.Callback.onKeyUp()}: perform clicking of the view
 * when {@link KeyEvent#KEYCODE_DPAD_CENTER}, {@link KeyEvent#KEYCODE_ENTER}
 * or {@link KeyEvent#KEYCODE_SPACE} is released.
 * <p>Key presses in software keyboards will generally NOT trigger this listener,
 * although some may elect to do so in some situations. Do not rely on this to
 * catch software key presses.
 *
 * @param keyCode A key code that represents the button pressed, from
 *                {@link android.view.KeyEvent}.
 * @param event   The KeyEvent object that defines the button action.
 */
public boolean onKeyUp(int keyCode, KeyEvent event) {
    android.util.SeempLog.record(5);
    if (KeyEvent.isConfirmKey(keyCode)) {
        if ((mViewFlags & ENABLED_MASK) == DISABLED) {
            return true;
        }
        if ((mViewFlags & CLICKABLE) == CLICKABLE && isPressed()) {
            setPressed(false);

            if (!mHasPerformedLongPress) {
                // This is a tap, so remove the longpress check
                removeLongPressCallback();
                if (!event.isCanceled()) {
                    return performClickInternal();//调用performClickInternal,响应View的click事件
                }
            }
        }
    }
    return false;
}

经过上面的几个核心类的源码查看,我们可以看出KeyEvent从上到下分发的具体流程:
ViewRootImpl$ViewPostImeInputStage.processKeyEvent===>DecorView.dispatchKeyEvent===>Activity.dispatchKeyEvent===>PhoneWindow.superDispatchKeyEvent===>DecorView.superDispatchKeyEvent===>ViewGroup.dispatchKeyEvent===>View.dispatchKeyEventt===>KeyEvent.dispatch===>View.onKeyUp.

也就是将KeyEvent依次传递:ViewRootImpl–DecorView–Activity–PhoneWindow–DecorView–ViewGroup–View–KeyEvent.dispatch–View.onKeyUp.这个非常明显,是从View的上层往下层传递的。

KeyEvent传递的时序图

在这里插入图片描述

Home 键的处理

Home键有一点特殊,我们可以看一下Home键的处理流程:

Home键的堆栈调用:

com.android.server.policy.PhoneWindowManager$DisplayHomeButtonHandler.handleHomeButton(PhoneWindowManager.java:1784)
com.android.server.policy.PhoneWindowManager.interceptKeyBeforeDispatchingInner(PhoneWindowManager.java:3090)
com.android.server.policy.PhoneWindowManager.interceptKeyBeforeDispatching(PhoneWindowManager.java:2955)
com.android.server.wm.InputManagerCallback.interceptKeyBeforeDispatching(InputManagerCallback.java:183)
com.android.server.input.InputManagerService.interceptKeyBeforeDispatching(InputManagerService.java:1900)

Home键调用的关键方法

  • InputManagerService.interceptKeyBeforeDispatching
// Native callback.
private long interceptKeyBeforeDispatching(IBinder focus, KeyEvent event, int policyFlags) {
    return mWindowManagerCallbacks.interceptKeyBeforeDispatching(focus, event, policyFlags);
}
  • InputManagerCallback.interceptKeyBeforeDispatching
/**
 * Provides an opportunity for the window manager policy to process a key before
 * ordinary dispatch.
 */
@Override
public long interceptKeyBeforeDispatching(
        IBinder focus, KeyEvent event, int policyFlags) {
    WindowState windowState = mService.windowForClientLocked(null, focus, false);
    return mService.mPolicy.interceptKeyBeforeDispatching(windowState, event, policyFlags);
}
  • PhoneWindowManager.interceptKeyBeforeDispatching
// TODO(b/117479243): handle it in InputPolicy
/** {@inheritDoc} */
@Override
public long interceptKeyBeforeDispatching(WindowState win, KeyEvent event, int policyFlags) {
    final long result = interceptKeyBeforeDispatchingInner(win, event, policyFlags);
    ......
  • PhoneWindowManager.interceptKeyBeforeDispatchingInner
private long interceptKeyBeforeDispatchingInner(WindowState win, KeyEvent event,
        int policyFlags) {
    final boolean keyguardOn = keyguardOn();
    final int keyCode = event.getKeyCode();
    final int repeatCount = event.getRepeatCount();
    final int metaState = event.getMetaState();
    final int flags = event.getFlags();
    final boolean down = event.getAction() == KeyEvent.ACTION_DOWN;
    final boolean canceled = event.isCanceled();
    final int displayId = event.getDisplayId();

    if (DEBUG_INPUT) {
        Log.d(TAG, "interceptKeyTi keyCode=" + keyCode + " down=" + down + " repeatCount="
                + repeatCount + " keyguardOn=" + keyguardOn + " canceled=" + canceled);
    }
    ......
    // First we always handle the home key here, so applications
    // can never break it, although if keyguard is on, we do let
    // it handle it, because that gives us the correct 5 second
    // timeout.
    if (keyCode == KeyEvent.KEYCODE_HOME) {//这里才是处理Home Key的逻辑片,事实上,许多Key都是在这个方法里处理的。
        if (getKeyCodeDispatchEnable()) {
            Slog.w("PhoneWindowManager", "Ignoring HOME because getKeyCodeDispatchEnable() =  true");
            return 0;
        }
        DisplayHomeButtonHandler handler = mDisplayHomeButtonHandlers.get(displayId);
        if (handler == null) {
            handler = new DisplayHomeButtonHandler(displayId);
            mDisplayHomeButtonHandlers.put(displayId, handler);
        }
        return handler.handleHomeButton(win, event);
  • PhoneWindowManager$DisplayHomeButtonHandler.handleHomeButton
int handleHomeButton(WindowState win, KeyEvent event) {
......
     // Delay handling home if a double-tap is possible.
     if (mDoubleTapOnHomeBehavior != DOUBLE_TAP_HOME_NOTHING) {
         mHandler.removeCallbacks(mHomeDoubleTapTimeoutRunnable); // just in case
         mHomeDoubleTapPending = true;
         mHandler.postDelayed(mHomeDoubleTapTimeoutRunnable,
                 ViewConfiguration.getDoubleTapTimeout());
         return -1;
     }

     // Post to main thread to avoid blocking input pipeline.
     mHandler.post(() -> handleShortPressOnHome(mDisplayId));
     return -1;
 }
......
     // Remember that home is pressed and handle special actions.
     if (repeatCount == 0) {
         mHomePressed = true;
         if (mHomeDoubleTapPending) {
             mHomeDoubleTapPending = false;
             mHandler.removeCallbacks(mHomeDoubleTapTimeoutRunnable);
             handleDoubleTapOnHome();
         // TODO(multi-display): Remove display id check once we support recents on
         // multi-display
         } else if (mDoubleTapOnHomeBehavior == DOUBLE_TAP_HOME_RECENT_SYSTEM_UI
                 && mDisplayId == DEFAULT_DISPLAY) {
             preloadRecentApps();
         }
     } else if ((event.getFlags() & KeyEvent.FLAG_LONG_PRESS) != 0) {
         if (!keyguardOn) {
             // Post to main thread to avoid blocking input pipeline.
             mHandler.post(() -> handleLongPressOnHome(event.getDeviceId()));
         }
     }
     return -1;
 }

Back键的处理

这是back键的调用堆栈

com.android.server.policy.PhoneWindowManager.interceptBackKeyUp(PhoneWindowManager.java:1042)
com.android.server.policy.PhoneWindowManager.interceptBackKeyDown(PhoneWindowManager.java:1021)
com.android.server.policy.PhoneWindowManager.interceptKeyBeforeQueueing(PhoneWindowManager.java:4191)
com.android.server.wm.InputManagerCallback.interceptKeyBeforeQueueing(InputManagerCallback.java:164)
com.android.server.input.InputManagerService.interceptKeyBeforeQueueing(InputManagerService.java:1888)
com.android.server.input.InputManagerService.nativeInjectInputEvent(Native Method)
com.android.server.input.InputManagerService.injectInputEventInternal(InputManagerService.java:643)
com.android.server.input.InputManagerService.injectInputEvent(InputManagerService.java:625)
android.hardware.input.IInputManager$Stub.onTransact(IInputManager.java:392)
android.os.Binder.execTransactInternal(Binder.java:1021)
android.os.Binder.execTransact(Binder.java:994)

我们重点查看几个方法:

  • PhoneWindowManager.interceptKeyBeforeQueueing
// TODO(b/117479243): handle it in InputPolicy
/** {@inheritDoc} */
@Override
public int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags) {
......
// Handle special keys.
switch (keyCode) {
    case KeyEvent.KEYCODE_BACK: {
        if (down) {
            interceptBackKeyDown();//调用interceptBackKeyDown
        } else {
            boolean handled = interceptBackKeyUp(event);//调用interceptBackKeyUp

            // Don't pass back press to app if we've already handled it via long press
            if (handled) {
                result &= ~ACTION_PASS_TO_USER;
            }
        }
        break;
    }
......
  • PhoneWindowManager.interceptBackKeyDown
private void interceptBackKeyDown() {
    mLogger.count("key_back_down", 1);
    // Reset back key state for long press
    mBackKeyHandled = false;

    if (hasLongPressOnBackBehavior()) {//长按back键
        Message msg = mHandler.obtainMessage(MSG_BACK_LONG_PRESS);
        msg.setAsynchronous(true);
        mHandler.sendMessageDelayed(msg,
                ViewConfiguration.get(mContext).getDeviceGlobalActionKeyTimeout());
    }
}
  • PhoneWindowManager.interceptBackKeyUp
// returns true if the key was handled and should not be passed to the user
private boolean interceptBackKeyUp(KeyEvent event) {
    mLogger.count("key_back_up", 1);
    // Cache handled state
    boolean handled = mBackKeyHandled;

    // Reset back long press state
    cancelPendingBackKeyAction();

    if (mHasFeatureWatch) {
        TelecomManager telecomManager = getTelecommService();

        if (telecomManager != null) {
            if (telecomManager.isRinging()) {
                // Pressing back while there's a ringing incoming
                // call should silence the ringer.
                telecomManager.silenceRinger();//来电静音

                // It should not prevent navigating away
                return false;
            } else if (
                (mIncallBackBehavior & Settings.Secure.INCALL_BACK_BUTTON_BEHAVIOR_HANGUP) != 0
                    && telecomManager.isInCall()) {
                // Otherwise, if "Back button ends call" is enabled,
                // the Back button will hang up any current active call.
                return telecomManager.endCall();//挂断来电电话
            }
        }
    }

    if (mAutofillManagerInternal != null && event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
        mHandler.sendMessage(mHandler.obtainMessage(MSG_DISPATCH_BACK_KEY_TO_AUTOFILL));
    }

    return handled;
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

hfreeman2008

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值