“This is the 10th day of my participation in the Gwen Challenge in November. Check out the details: The Last Gwen Challenge in 2021.”

KeyEvent distribution process

In Tv development, dispatchKeyEvent, onKeyDown/Up, onKeyLisenter and so on are used to distribute processing. Unlike touch phones, which are dispatched with dispatchTouchEvent, onInterceptTouchEvent and onTouchEvent. The main View tree internal event distribution process.


When we click the button of the remote control, there will be two KeyEvent ACTION_DOWN and ACTION_UP for distribution, and their distribution process is the same.

1. PhoneWindow (DecorView dispatchKeyEvent)

When a KeyEvent event is received, the dispatchKeyEvent method is first passed to the DecorView for event distribution.

/frameworks/base/core/java/com/android/internal/policy/DecorView.java$dispatchKeyEvent

 if(! mWindow.isDestroyed()) {final Window.Callback cb = mWindow.getCallback();
     final booleanhandled = cb ! =null && mFeatureId < 0 ? cb.dispatchKeyEvent(event)
         : super.dispatchKeyEvent(event);
     if (handled) {
         return true; }}Copy the code

Attach method for Activity:

frameworks/base/core/java/android/app/Activity.java$attach

mWindow.setCallback(this);
Copy the code

The Activity implements the window. Callback interface, so cb represents the current Activity. So this is actually a dispatchKeyEvent for the Activity that’s called, and it’s sent to the Activity to handle.

2. The Activity dispatchKeyEvent

frameworks/base/core/java/android/app/Activity.java$dispatchKeyEvent

Window win = getWindow();
if (win.superDispatchKeyEvent(event)) {
    return true;
}
Copy the code

The Activity first gets the PhoneWindow object through getWindow(); Call PhoneWindow’s superDispatchKeyEvent method.

frameworks/base/core/java/com/android/internal/policy/PhoneWindow.java$superDispatchKeyEvent

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

PhoneWindow does nothing else and instead calls the DecorView superDispatchKeyEvent

3. The ViewGroup dispatchKeyEvent

/frameworks/base/core/java/com/android/internal/policy/DecorView.java$superDispatchKeyEvent

if (super.dispatchKeyEvent(event)) {
    return true;
}
Copy the code

The DecorView calls the super.dispatchKeyevent (event) parent. The DecorView inherits from the ViewGroup, so it actually hands the dispatchKeyEvent to the ViewGroup for event distribution.

/frameworks/base/core/java/android/view/ViewGroup.java$dispatchKeyEvent

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;
    }
Copy the code

The ViewGroup recursively looks for the child View of the current focus and passes the event to the child View’s dispatchKeyEvent distribution.

4. Process

KeyEvent is handled in two places: an Activity and a View.

The ViewGroup is only responsible for distribution. Event returns True to indicate that the event was consumed.

The View of dispatchKeyEvent

/frameworks/base/core/java/android/view/KeyEvent.java

When dispatchKeyEvent is sent to the View, it checks whether the Listener for the Key is set. If so, it calls onKeyListener.onkey ().

ListenerInfo li = mListenerInfo;
if(li ! =null&& li.mOnKeyListener ! =null && (mViewFlags & ENABLED_MASK) == ENABLED
    && li.mOnKeyListener.onKey(this, event.getKeyCode(), event)) {
    return true;
}
Copy the code

If OnKeyListener is not set or onKeyListener.onkey () returns false, the View will call KeyEvent’s dispatch callback on the View’s onKeyDown/Up time.

if (event.dispatch(this, mAttachInfo ! =null
                   ? mAttachInfo.mKeyDispatchState : null.this)) {
    return true;
}
​
if(mInputEventConsistencyVerifier ! =null) {
    mInputEventConsistencyVerifier.onUnhandledEvent(event, 0);
}
Copy the code

The KeyEvent dispatch

 boolean res = receiver.onKeyDown(mKeyCode, this);
Copy the code

Because it is the dispatch of the KeyEvent called by the view, it calls back the view’s own KeyDown or KeyUp event via KeyEvent. The Activity in the same way.

The View of the onKeyUp

If the View’s onKeyUp method is not overwritten and keyevent.isconfirmKey (keyCode) is true, confirm the ACTION_UP key

 public boolean onKeyUp(int keyCode, KeyEvent event) {
     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()) {returnperformClickInternal(); }}}}return false;
    }
Copy the code

PerformClickInternal () checks for an OnClickListener and consumes the event if it does

If you have an OnClickListener on the View and the event is not consumed on it, it will be consumed in onClick(). When the View dispatchKeyEvent() dispatches the event to onClick, it already returns true by default indicating that the event was consumed.

If no event is handled in the View, that is, no OnClickListener is listening, or if KeyDown and KeyUp return false, the event will be returned to the Activity and will call its own KeyDown and KeyUp events, handing the event to the Activity to handle.

The Activity overrides dispatchKeyEvent and returns true or false to intercept the event.