Android 应用层Input事件分发机制

104 阅读3分钟

基于Android R版本分析

touch 事件机制

touch事件分发机制.png

上述的流程在touch事件处理中可以分为两个模块:

  • Window窗口拦截消费touch事件;
  • View拦截消费touch事件;

上述的判断逻辑在Activity的dispatchTouchEvent()方法中叉分:

/**
 * Called to process touch screen events.  You can override this to
 * intercept all touch screen events before they are dispatched to the
 * window.  Be sure to call this implementation for touch screen events
 * that should be handled normally.
 *
 * @param ev The touch screen event.
 *
 * @return boolean Return true if this event was consumed.
 */
public boolean dispatchTouchEvent(MotionEvent ev) {
    if (ev.getAction() == MotionEvent.ACTION_DOWN) {
        onUserInteraction();
    }
    // 代表了Window窗口中的View子控件消费Touch事件
    if (getWindow().superDispatchTouchEvent(ev)) {
        return true;
    }
    // 如果没有View消费touch事件,则由Window窗口自身消费,调用Activity中的onTouchEvent()
    return onTouchEvent(ev);
}

InputStage

onTouchEvent:94, MainActivity (com.example.android.pictureinpicture)
dispatchTouchEvent:4128, Activity (android.app)
dispatchTouchEvent:69, WindowCallbackWrapper (androidx.appcompat.view)
dispatchTouchEvent:446, DecorView (com.android.internal.policy)
dispatchPointerEvent:14568, View (android.view)
processPointerEvent:6022, ViewRootImpl$ViewPostImeInputStage (android.view)
onProcess:5825, ViewRootImpl$ViewPostImeInputStage (android.view)
deliver:5316, ViewRootImpl$InputStage (android.view)
// ViewPostImeInputStage
onDeliverToNext:5373, ViewRootImpl$InputStage (android.view)
forward:5339, ViewRootImpl$InputStage (android.view)
forward:5491, ViewRootImpl$AsyncInputStage (android.view)
apply:5347, ViewRootImpl$InputStage (android.view)
apply:5548, ViewRootImpl$AsyncInputStage (android.view)
deliver:5320, ViewRootImpl$InputStage (android.view)
// NativePostImeInputStage
onDeliverToNext:5373, ViewRootImpl$InputStage (android.view)
forward:5339, ViewRootImpl$InputStage (android.view)
apply:5347, ViewRootImpl$InputStage (android.view)
deliver:5320, ViewRootImpl$InputStage (android.view)
// EarlyPostImeInputStage
deliverInputEvent:8086, ViewRootImpl (android.view)
doProcessInputEvents:8037, ViewRootImpl (android.view)
enqueueInputEvent:7998, ViewRootImpl (android.view)
onInputEvent:8209, ViewRootImpl$WindowInputEventReceiver (android.view)
dispatchInputEvent:220, InputEventReceiver (android.view)
nativePollOnce:-1, MessageQueue (android.os)
next:335, MessageQueue (android.os)
loop:183, Looper (android.os)
main:7666, ActivityThread (android.app)
invoke:-1, Method (java.lang.reflect)
run:592, RuntimeInit$MethodAndArgsCaller (com.android.internal.os)
main:947, ZygoteInit (com.android.internal.os)

上述的流程堆栈中,出现了3次deliver()方法的调用,对应的都是InputStage类的方法,但是每一次的InputStage都对应的不用类型的InputStage;

InputStage代表了输入事件的处理阶段,使用"责任链模式"设计模式;

InputStage 类图

InputStage.png

  • AsyncInputStage

    • NativePreImeInputStage:本地方法预处理输入法事件阶段(不支持Touch事件);
    • NativePostImeInputStage:本地方法处理阶段,主要构建了可延迟的队列(支持Touch事件);
    • ImeInputStage:输入法事件处理阶段,处理输入法字符(不支持Touch事件);
  • ViewPreImeInputStage:视图预处理输入法事件阶段,调用视图view的dispatchKeyEventPreIme方法(不支持Touch事件);

  • EarlyPostImeInputStage:输入法早期处理阶段(支持Touch事件);

  • ViewPostImeInputStage:视图输入处理阶段,比如按键、手指触摸等运动事件,我们熟知的view事件分发就发生在这个阶段(支持Touch事件);

  • SyntheticInputStage:综合处理事件阶段,比如处理导航面板、操作杆等事件,未处理 InputEvent最后处理(支持Touch事件);

InputStage流程图

InputStage的处理流程.png

责任链模式的各个InputStage进行说明,整体流程如上所说,简要归纳如下:

  1. 输入法之前的处理

    • NativePreImeInputStage
    • ViewPreImeInputStage
  2. 输入法处理

    • ImeInputStage
  3. 输入法之后处理

    • EarlyPostImeInputStage
    • NativePostImeInputStage
    • ViewPostImeInputStage
  4. 综合处理

    • SyntheticInputStage

InputStage将输入事件的处理分成若干个阶段(Stage),如果当前有输入法窗口,则事件处理从 NativePreIme 开始,否则从EarlyPostIme 开始;

事件依次经过每个Stage,如果该事件没有被标识为 “Finished”, 该Stage就会处理它,然后返回处理结果Forward 或 Finish,Forward 运行下一个Stage继续处理,而Finished事件将会简单的Forward到下一级,直到最后一级 SyntheticInputStage;

Window touch事件流转

自定义Activity中重现onTouchEvent()方法,该方法的调用栈;

input_view_.png

可以看到,在截止到ViewPostImeInputStage进行了touch事件的处理之后,不会再向下传递;