Event distribution process from Android Framework client ViewRootImpl to View

Posted by tilde on Wed, 22 Dec 2021 02:28:53 +0100

Overall process of event distribution

ViewRootImpl is followed by the timing of the client. This paper mainly focuses on the process of event distribution in the client

ViewRootImpl distribution event

dispatchInputEvent(InputEvent event) is the starting point of ViewRootImpl distribution event, and event is the input event

  1. After receiving the input event, ViewRootImpl will add it to the message queue using enqueueInputEvent(), and obtainQueuedInputEvent() will convert the input event into the QueuedInputEvent data structure required by the queue
  2. All messages from the message queue are handled by the deliverInputEvent method in doProcessInputEvents()
  3. The deliverInputEvent() method uses the InputStage linked list structure and the responsibility chain mode to traverse and transfer events

On Android view. In viewrootimpl#deliverinputevent, the relevant code is implemented as follows

InputStage stage;
if (q.shouldSendToSynthesizer()) {
    stage = mSyntheticInputStage;
} else {
    stage = q.shouldSkipIme() ? mFirstPostImeInputStage : mFirstInputStage;
}

if (q.mEvent instanceof KeyEvent) {
    Trace.traceBegin(Trace.TRACE_TAG_VIEW, "preDispatchToUnhandledKeyManager");
    try {
        mUnhandledKeyManager.preDispatch((KeyEvent) q.mEvent);
    } finally {
        Trace.traceEnd(Trace.TRACE_TAG_VIEW);
    }
}

if (stage != null) {
    handleWindowFocusChanged();
    stage.deliver(q);
} else {
    finishInputEvent(q);
}

InputStage is in viewrootimpl Java #setview (view, WindowManager. Layoutparams, view, int) is initialized

// Set up the input pipeline.
CharSequence counterSuffix = attrs.getTitle();
mSyntheticInputStage = new SyntheticInputStage();
InputStage viewPostImeStage = new ViewPostImeInputStage(mSyntheticInputStage);
InputStage nativePostImeStage = new NativePostImeInputStage(viewPostImeStage,
                                                            "aq:native-post-ime:" + counterSuffix);
InputStage earlyPostImeStage = new EarlyPostImeInputStage(nativePostImeStage);
InputStage imeStage = new ImeInputStage(earlyPostImeStage,
                                        "aq:ime:" + counterSuffix);
InputStage viewPreImeStage = new ViewPreImeInputStage(imeStage);
InputStage nativePreImeStage = new NativePreImeInputStage(viewPreImeStage,
                                                          "aq:native-pre-ime:" + counterSuffix);

mFirstInputStage = nativePreImeStage;
mFirstPostImeInputStage = earlyPostImeStage;

SyntheticInputStage and ViewPostImeInputStage inherit from InputStage, and rewrite the following construction method

public InputStage(InputStage next) {
    mNext = next;
}

It can be seen that different inputstages build a linked list structure, and its head node is referenced by mFirstInputStage. Each node contains a next pointer to the next InputStage

Here is a brief description of each InputStage:

InputStageexplain
NativePreImeInputStageDistribute inputevents earlier than IME to NativeActivity for processing. NativeActivity has the same functions as ordinary acitivity, but it is implemented in the native layer, so the execution efficiency will be higher. At the same time, NativeActivity is very practical in game development (does not support touch events).
ViewPreIMEInputStageWhen inputevents earlier than IME are distributed to the view framework for processing, the onkeypreime method of view (input focus) will be called. At the same time, the view will get messages before the input method processes key events and give priority to processing. View series controls can directly copy onkeypreime (touch events are not supported).
ImeInputStageDistribute InputEvent to IME for processing, call onProcess of ImeInputStage, and the dispatchInputEvent method of InputMethodManager for processing messages (touch events are not supported).
EarlyPostImeInputStageIt is related to touchmode. For example, your mobile phone has a direction key. Pressing the direction key will exit touchmode. This event is consumed. There may be a change in the background of the view, but it is uncertain (touch events are supported).
NativePostImeInputStageDistribute the InputEvent event to the nativeactivity. After the IME processes the message, it can process the message before the normal Activity (touch event is supported at this time).
ViewPostImeInputStageDistribute the InputEvent event to the view framework, and the event distribution of view (supports touch events). Finally, three methods will be called to the input focus: onKey of the listener registered with setKeyListener, followed by onKeyDown and onKeyUp, or onKeyDown and onKeyUp methods of activity, that is, handle the unattended key events.
SyntheticInputStageInputEvent was not processed.

On Android view. In viewrootimpl#deliverinputevent, if the stage is not null, Android. Net will be executed view. ViewRootImpl. Inputstage#deliver method

if (stage != null) {
    handleWindowFocusChanged();
    stage.deliver(q);
} else {
    finishInputEvent(q);
}

android. view. ViewRootImpl. The inputstage#deliver method is implemented as follows

public final void deliver(QueuedInputEvent q) {
    if ((q.mFlags & QueuedInputEvent.FLAG_FINISHED) != 0) {
        forward(q);
    } else if (shouldDropInputEvent(q)) {
        finish(q, false);
    } else {
        traceEvent(q, Trace.TRACE_TAG_VIEW);
        final int result;
        try {
            result = onProcess(q);
        } finally {
            Trace.traceEnd(Trace.TRACE_TAG_VIEW);
        }
        apply(q, result);
    }
}

Events pass through each Stage in turn. If the event is not marked as "Finished", the Stage will process it, and then return the processing result forward or Finish. Forward runs the next Stage to continue processing, and Finished will set the "Finished" mark on the event and advance to the next Stage until the last level of SyntheticInputStage.

Stage calls onProcess to process its events. Template mode is adopted here. onProcess is a hook method. Different subclasses have different implementations. For touch screen events and key press events, ViewPostImeInputStage is mainly used to process them.

ViewPostImeInputStage

android.view.ViewRootImpl.ViewPostImeInputStage#onProcess

@Override
protected int onProcess(QueuedInputEvent q) {
    if (q.mEvent instanceof KeyEvent) {
        return processKeyEvent(q);
    } 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);
        }
    }
}

The onProcess method is divided according to different event types. The touch-screen event goes through processPointerEvent(), and the key event goes through processKeyEvent()

Take a touch screen event as an example,

android.view.ViewRootImpl.ViewPostImeInputStage#processPointerEvent

private int processPointerEvent(QueuedInputEvent q) {
    final MotionEvent event = (MotionEvent)q.mEvent;

    mAttachInfo.mUnbufferedDispatchRequested = false;
    mAttachInfo.mHandlingPointerEvent = true;
    boolean handled = mView.dispatchPointerEvent(event);
    maybeUpdatePointerIcon(event);
    maybeUpdateTooltip(event);
    mAttachInfo.mHandlingPointerEvent = false;
    if (mAttachInfo.mUnbufferedDispatchRequested && !mUnbufferedInputDispatch) {
        mUnbufferedInputDispatch = true;
        if (mConsumeBatchedInputScheduled) {
            scheduleConsumeBatchedInputImmediately();
        }
    }
    return handled ? FINISH_HANDLED : FORWARD;
}

Where Boolean handled = Mview dispatchPointerEvent(event); Transfer the event to the View under ViewRootImpl for processing

android.view.View#dispatchPointerEvent

@UnsupportedAppUsage
public final boolean dispatchPointerEvent(MotionEvent event) {
    if (event.isTouchEvent()) {
        return dispatchTouchEvent(event);
    } else {
        return dispatchGenericMotionEvent(event);
    }
}

Judge in dispatchPointerEvent(). If it is a touch event, go to dispatchTouchEvent(), otherwise execute dispatchGenericMotionEvent()

The sequence diagram of the above process is

Reference blog

https://www.cnblogs.com/huan89/p/14286463.html

http://gityuan.com/2016/12/31/input-ipc/

https://blog.csdn.net/vviccc/article/details/93377708

Topics: Java Android