Android 输入事件分发全流程梳理(二)

前言:书接上回,上回梳理了SystemServer进程的输入管理服务(InputManagerService)的初始化,讲解了native层从接收到事件到分发事件到Java层的流程。本章节将从ViewRootImplWindowInputEventReceiver开始讲起,重点梳理客户端app进程内对于输入事件的分发与消费。

1. ViewRootImpl

在应用进程的Java层中,最先收到输入事件的Java类就是ViewRootImpl的内部类WindowInputEventReceiverWindowInputEventReceiver继承自抽象类InputEventReceiver,上回说到客户端进程的native层调用了InputEventReceiver对象的dispatchInputEvent方法:

InputEventReceiver.java
private void dispatchInputEvent(int seq, InputEvent event) {
    mSeqMap.put(event.getSequenceNumber(), seq);
    onInputEvent(event);
}
ViewRootImpl.java
final class WindowInputEventReceiver extends InputEventReceiver {
    @Override
    public void onInputEvent(InputEvent event) {
        ...
        //这个对象用于M版本以下的兼容,M版本及以上默认为null
        if (processedEvents != null) {
            ...
        } else {
            enqueueInputEvent(event, this, 0, true);
        }
    }
}

enqueueInputEventViewRootImpl这个类的方法,负责将新收到的输入事件插入一个待处理输入事件队列的队尾,并分别用mPendingInputEventHeadmPendingInputEventTail指向队头和队尾,如果没有待处理输入事件,则这个新收到的输入事件就同时是队头和队尾了:

ViewRootImpl.java
void enqueueInputEvent(InputEvent event,
        InputEventReceiver receiver, int flags, boolean processImmediately) {
    ...
    QueuedInputEvent last = mPendingInputEventTail;
    if (last == null) {
        mPendingInputEventHead = q;
        mPendingInputEventTail = q;
    } else {
        last.mNext = q;
        mPendingInputEventTail = q;
    }
    mPendingInputEventCount += 1;
    if (processImmediately) {
        doProcessInputEvents();
    } else {
        scheduleProcessInputEvents();
    }
}

void doProcessInputEvents() {
    while (mPendingInputEventHead != null) {
        //从待处理输入事件队列的队头取出输入事件
        QueuedInputEvent q = mPendingInputEventHead;
        mPendingInputEventHead = q.mNext;
        if (mPendingInputEventHead == null) {
            mPendingInputEventTail = null;
        }
        q.mNext = null;

        mPendingInputEventCount -= 1;
    
        mViewFrameInfo.setInputEvent(mInputEventAssigner.processEvent(q.mEvent));
        //继续传递输入事件
        deliverInputEvent(q);
    }
    ...
}

private void deliverInputEvent(QueuedInputEvent q) {
    ...
    InputStage stage;
    //是否不交由后续流程处理,而是直接内部处理
    if (q.shouldSendToSynthesizer()) {
        stage = mSyntheticInputStage;
    } else {
        //正常流程走这里,判断是否需要优先传给输入法处理
        stage = q.shouldSkipIme() ? mFirstPostImeInputStage : mFirstInputStage;
    }
    ...
    if (stage != null) {
        handleWindowFocusChanged();
        stage.deliver(q);
    } else {
        finishInputEvent(q);
    }
    ...
}

我们考虑正常的屏幕Touch事件传输流程,则不会优先传给输入法处理,而是使用mFirstPostImeInputStagedeliver方法进行处理。这一套处理流程是采用了类似责任链的设计模式,可以看一下是怎么设计的:

ViewRootImpl.java
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView, int userId)) {
    ...
    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;
    ...
}

这一系列InputStage通过将其自身作为另一个InputStage构造函数的参数传进去,对于每个事件,依次流转看看哪个InputStage能够消费该事件,所以事件的传递流程是:
EarlyPostImeInputStage -> NativePostImeInputStage -> ViewPostImeInputStage -> SyntheticInputStage
其中每个环节如果没有自己消费掉事件,就会传递给下一环节进行处理,如果消费了,则不会给下一环节进行处理。

2. ViewRootImpl -> DecorView -> Activity

我们直接看ViewPostImeInputStage的事件处理,因为我们熟悉的Activity、View等收到的输入事件就是从这个环节分发下去的:

ViewRootImpl.java
final class ViewPostImeInputStage extends InputStage {
    @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);
            }
        }
    }
    
    private int processPointerEvent(QueuedInputEvent q) {
        final MotionEvent event = (MotionEvent)q.mEvent;
        //先分发给手写模式处理,判断是否是手写区域的事件
        boolean handled = mHandwritingInitiator.onTouchEvent(event);
        if (handled) {
            mLastClickToolType = event.getToolType(event.getActionIndex());
        }
        
        mAttachInfo.mUnbufferedDispatchRequested = false;
        mAttachInfo.mHandlingPointerEvent = true;
        //如果手写模式处理器没有消费该事件,则将事件分发给DecorView
        handled = handled || mView.dispatchPointerEvent(event);
        maybeUpdatePointerIcon(event);
        maybeUpdateTooltip(event);
        mAttachInfo.mHandlingPointerEvent = false;
        if (mAttachInfo.mUnbufferedDispatchRequested && !mUnbufferedInputDispatch) {
            mUnbufferedInputDispatch = true;
            if (mConsumeBatchedInputScheduled) {
                scheduleConsumeBatchedInputImmediately();
            }
        }
        //如果View选择消费,则事件分发到此为止,否则继续向下个环节分发。
        return handled ? FINISH_HANDLED : FORWARD;
    }
}

ViewPostImeInputStage中,会将事件分发给DecorViewdispatchPointerEvent,这是父类View的方法,但DecorView并没有重写,所以回到View里看:

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

如果是touch输入事件,则传给dispatchTouchEvent方法,这个方法DecorView重写了:

DecorView.java
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
    final Window.Callback cb = mWindow.getCallback();
    return cb != null && !mWindow.isDestroyed() && mFeatureId < 0
            ? cb.dispatchTouchEvent(ev) : super.dispatchTouchEvent(ev);
}

这个Window.CallBack其实是Activty对象,Activity实现了这个接口,在Activity启动时的attach方法里,将自身作为Window.CallBack传给PhoneWindow:

Activity.java
final void attach(...) {
    ...
    mWindow = new PhoneWindow(this, window, activityConfigCallback);
    ...
    mWindow.setCallback(this);
    ...
}

所以紧接着我们看到ActivitydispatchTouchEvent方法:

Activity.java
public boolean dispatchTouchEvent(MotionEvent ev) {
    if (ev.getAction() == MotionEvent.ACTION_DOWN) {
        onUserInteraction();
    }
    if (getWindow().superDispatchTouchEvent(ev)) {
        return true;
    }
    return onTouchEvent(ev);
}

Activity收到输入事件后会优先将其传给PhoneWindowsuperDispatchTouchEvent方法处理,其内部就是传回给DecorView继续处理。
但如果都没消费该事件,则最后会交给Activity自己的onTouchEvent里判断是否需要消费。

PhoneWindow.java
@Override
public boolean superDispatchTouchEvent(MotionEvent event) {
    return mDecor.superDispatchTouchEvent(event);
}
DecorView.java
public boolean superDispatchTouchEvent(MotionEvent event) {
    return super.dispatchTouchEvent(event);
}

这里事件重新回到了DecorView里进一步进行分发,DecorView继承自FrameLayout,是Activity布局的顶层View,本质就是一个ViewGroup
所以从这里开始,输入事件进入了从ViewGroup -> View的分发流程了。

3. ViewGroup -> View

DecorView的父类FrameLayout并没有重写dispatchTouchEvent这个方法,所以我们继续向上追溯FrameLayout的父类ViewGroup
ViewGroupdispatchTouchEvent这个方法非常重要,建议想要熟悉事件分发的同学一定要一行行代码理解清楚,面试官想要问得深一点就会从这里面入手,所以我们这里就不省略代码了,而是详细带大家一起看看:

ViewGroup.java
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
    //调试用,检查输入事件序列的自洽性,忽略
    if (mInputEventConsistencyVerifier != null) {
        mInputEventConsistencyVerifier.onTouchEvent(ev, 1);
    }
    //无障碍用
    if (ev.isTargetAccessibilityFocus() && isAccessibilityFocusedViewOrHost()) {
        ev.setTargetAccessibilityFocus(false);
    }
    boolean handled = false;
    //判断触摸事件是否符合安全策略,该方法可重写以定制
    if (onFilterTouchEventForSecurity(ev)) {
        final int action = ev.getAction();
        final int actionMasked = action & MotionEvent.ACTION_MASK;
        
        //如果是Down事件,重置本地一些变量、状态
        if (actionMasked == MotionEvent.ACTION_DOWN) {
            cancelAndClearTouchTargets(ev);
            resetTouchState();
        }

        //下面就是判断是否需要拦截该事件的代码块
        final boolean intercepted;
        //如果是Down事件或者之前已经有子View消费了触摸事件,则才进入拦截判断流程,否则直接进行拦截,不再传给子View
        //所以如果之前没有child消费Down事件,则后续的非Down事件默认采取拦截策略
        if (actionMasked == MotionEvent.ACTION_DOWN || mFirstTouchTarget != null) {
            //判断是否被设置了FLAG_DISALLOW_INTERCEPT这个标志位,外部可调用requestDisallowInterceptTouchEvent方法设置该标志位
            final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
            //如果没有设置该标志位,则需要判断是否拦截,否则就跳过拦截步骤
            if (!disallowIntercept) {
                //在onInterceptTouchEvent中判断是否需要拦截,自定义ViewGroup可重写以定制
                intercepted = onInterceptTouchEvent(ev);
                ev.setAction(action);//重设action以防在上述步骤中被修改
            } else {
                intercepted = false;
            }
        } else {
            intercepted = true;
        }

        if (intercepted || mFirstTouchTarget != null) {
            ev.setTargetAccessibilityFocus(false);
        }
        
        //检查该事件是否该取消,不再处理,比如处于detached状态或者本身就是取消事件
        final boolean canceled = resetCancelNextUpFlag(this) || actionMasked == MotionEvent.ACTION_CANCEL;

        final boolean isMouseEvent = ev.getSource() == InputDevice.SOURCE_MOUSE;
        final boolean split = (mGroupFlags & FLAG_SPLIT_MOTION_EVENTS) != 0 && !isMouseEvent;
        //指向消费该事件的目标View
        TouchTarget newTouchTarget = null;
        boolean alreadyDispatchedToNewTouchTarget = false;
        //当事件没有被取消并且并没有本Viewgroup没有选择拦截该事件,则下面考虑分发给子View
        if (!canceled && !intercepted) {
            //如果该事件是用来给无障碍焦点目标处理的,则我们会将其给具有无障碍焦点的View进行处理
            View childWithAccessibilityFocus = ev.isTargetAccessibilityFocus()
                    ? findChildWithAccessibilityFocus() : null;
            //这里我们重点关注Down事件的特殊处理
            if (actionMasked == MotionEvent.ACTION_DOWN
                        || (split && actionMasked == MotionEvent.ACTION_POINTER_DOWN)
                        || actionMasked == MotionEvent.ACTION_HOVER_MOVE) {
                final int actionIndex = ev.getActionIndex();
                final int idBitsToAssign = split ? 1 << ev.getPointerId(actionIndex) : TouchTarget.ALL_POINTER_IDS;

                removePointersFromTouchTargets(idBitsToAssign);

                final int childrenCount = mChildrenCount;
                if (newTouchTarget == null && childrenCount != 0) {
                    final float x = ev.getXDispatchLocation(actionIndex);
                    final float y = ev.getYDispatchLocation(actionIndex);
                    //ViewGroup支持自定义子View们的绘制顺序,这个顺序也会影响到事件分发的顺序
                    final ArrayList<View> preorderedList = buildTouchDispatchChildList();
                    final boolean customOrder = preorderedList == null && isChildrenDrawingOrderEnabled();
                    final View[] children = mChildren;
                    //从前往后扫描所有子View,找到能够接收到该事件的child
                    for (int i = childrenCount - 1; i >= 0; i--) {
                        final int childIndex = getAndVerifyPreorderedIndex(childrenCount, i, customOrder);
                        final View child = getAndVerifyPreorderedView(preorderedList, children, childIndex);

                        //如果优先传给无障碍焦点目标,则遍历一直找到该View,如果不是这按照正常的事件分发流程进行
                        if (childWithAccessibilityFocus != null) {
                            if (childWithAccessibilityFocus != child) {
                                continue;
                            }
                            childWithAccessibilityFocus = null;
                            i = childrenCount;
                        }

                        //判断该child是否无法接收该事件,比如不满足可见性要求、事件坐标不在该child显示区域内
                        if (!child.canReceivePointerEvents() || !isTransformedTouchPointInView(x, y, child, null)) {
                            ev.setTargetAccessibilityFocus(false);
                            continue;
                        }

                        //先找到上次消费了touch事件的child,如果有,则将newTouchTarget指向它,还是指定它去处理事件,并跳出遍历
                        newTouchTarget = getTouchTarget(child);
                        if (newTouchTarget != null) {
                            newTouchTarget.pointerIdBits |= idBitsToAssign;
                            break;
                        }

                        resetCancelNextUpFlag(child);
                        //将事件传给child的dispatchTouchEvent处理,如果返回true即代表child已消费
                        if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
                            mLastTouchDownTime = ev.getDownTime();
                            if (preorderedList != null) {
                                for (int j = 0; j < childrenCount; j++) {
                                    if (children[childIndex] == mChildren[j]) {
                                        mLastTouchDownIndex = j;
                                        break;
                                    }
                                }
                            } else {
                                mLastTouchDownIndex = childIndex;
                            }
                            mLastTouchDownX = x;
                            mLastTouchDownY = y;
                            //将newTouchTarget指向消费该事件的child,并且mFirstTouchTarget也会指向该child
                            newTouchTarget = addTouchTarget(child, idBitsToAssign);
                            alreadyDispatchedToNewTouchTarget = true;
                            break;
                        }

                        ev.setTargetAccessibilityFocus(false);
                    }
                    if (preorderedList != null) preorderedList.clear();
                }

                //如果本次Down事件没有child选择消费,则考虑传给之前消费过Down事件的child试试,多指触摸会存在这种情况
                if (newTouchTarget == null && mFirstTouchTarget != null) {
                    newTouchTarget = mFirstTouchTarget;
                    while (newTouchTarget.next != null) {
                        newTouchTarget = newTouchTarget.next;
                    }
                    newTouchTarget.pointerIdBits |= idBitsToAssign;
                }
            }
        }
        
        //分发事件给目标View,上述的逻辑已经筛选出是否存在可以消费该事件的目标View了
        if (mFirstTouchTarget == null) {
            //没有任何可以消费该事件的child,则将本ViewGroup看作一个普通的View,传给super.dispatchTouchEvent处理
            handled = dispatchTransformedTouchEvent(ev, canceled, null, TouchTarget.ALL_POINTER_IDS);
        } else {
            //存在可以消费该事件的child,有可能已经消费了,也有可能还没消费
            TouchTarget predecessor = null;
            TouchTarget target = mFirstTouchTarget;
            while (target != null) {
                final TouchTarget next = target.next;
                //在上面的代码块里child有可能已经消费该事件了
                if (alreadyDispatchedToNewTouchTarget && target == newTouchTarget) {
                    handled = true;
                } else {
                    final boolean cancelChild = resetCancelNextUpFlag(target.child) || intercepted;
                    //将touch事件传给child的dispatchTouchEvent方法
                    if (dispatchTransformedTouchEvent(ev, cancelChild, target.child, target.pointerIdBits)) {
                        handled = true;
                    }
                    //如果本次拦截该事件,则将cancel事件传给之前所有消费过Down事件的child
                    if (cancelChild) {
                        if (predecessor == null) {
                            mFirstTouchTarget = next;
                        } else {
                            predecessor.next = next;
                        }
                        target.recycle();
                        target = next;
                        continue;
                    }
                }
                //如果不拦截该事件,则将本事件依次传给之前所有消费过Down事件的child,不管消不消费都进行分发
                predecessor = target;
                target = next;
            }
        }
        //如果是取消事件、Up事件或者悬停移动事件,则清除掉之前保存的已消费事件的View列表
        if (canceled
                || actionMasked == MotionEvent.ACTION_UP
                || actionMasked == MotionEvent.ACTION_HOVER_MOVE) {
            resetTouchState();
        } else if (split && actionMasked == MotionEvent.ACTION_POINTER_UP) {
            final int actionIndex = ev.getActionIndex();
            final int idBitsToRemove = 1 << ev.getPointerId(actionIndex);
            removePointersFromTouchTargets(idBitsToRemove);
        }
    }
    if (!handled && mInputEventConsistencyVerifier != null) {
        mInputEventConsistencyVerifier.onUnhandledEvent(ev, 1);
    }
    return handled;
}

/**
* child为空,则将事件传给child的dispatchTouchEvent方法,否则就默认传给本ViewGroup的super.dispatchTouchEvent方法
*/
private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel, View child, int desiredPointerIdBits) {
    final boolean handled;

    final int oldAction = event.getAction();
    if (cancel || oldAction == MotionEvent.ACTION_CANCEL) {
        event.setAction(MotionEvent.ACTION_CANCEL);
        if (child == null) {
            handled = super.dispatchTouchEvent(event);
        } else {
            handled = child.dispatchTouchEvent(event);
        }
        event.setAction(oldAction);
        return handled;
    }

    final int oldPointerIdBits = event.getPointerIdBits();
    final int newPointerIdBits = oldPointerIdBits & desiredPointerIdBits;

    if (newPointerIdBits == 0) {
        return false;
    }

    final MotionEvent transformedEvent;
    if (newPointerIdBits == oldPointerIdBits) {
        if (child == null || child.hasIdentityMatrix()) {
            if (child == null) {
                handled = super.dispatchTouchEvent(event);
            } else {
                final float offsetX = mScrollX - child.mLeft;
                final float offsetY = mScrollY - child.mTop;
                event.offsetLocation(offsetX, offsetY);

                handled = child.dispatchTouchEvent(event);

                event.offsetLocation(-offsetX, -offsetY);
            }
            return handled;
        }
        transformedEvent = MotionEvent.obtain(event);
    } else {
        transformedEvent = event.split(newPointerIdBits);
    }

    if (child == null) {
        handled = super.dispatchTouchEvent(transformedEvent);
    } else {
        final float offsetX = mScrollX - child.mLeft;
        final float offsetY = mScrollY - child.mTop;
        transformedEvent.offsetLocation(offsetX, offsetY);
        if (! child.hasIdentityMatrix()) {
            transformedEvent.transform(child.getInverseMatrix());
        }

        handled = child.dispatchTouchEvent(transformedEvent);
    }

    transformedEvent.recycle();
    return handled;
}
View.java
public boolean dispatchTouchEvent(MotionEvent event) {
    ...
    //如果该View设置了mOnTouchListener,则优先传给onTouch,不选择消费才会传给onTouchEvent方法
    if (onFilterTouchEventForSecurity(event)) {
        if ((mViewFlags & ENABLED_MASK) == ENABLED && handleScrollBarDragging(event)) {
            result = true;
        }
        ListenerInfo li = mListenerInfo;
        if (li != null && li.mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED && li.mOnTouchListener.onTouch(this, event)) {
            result = true;
        }

        if (!result && onTouchEvent(event)) {
            result = true;
        }
    }
    ...

    return result;
}

整个流程涉及到几个重要流转方法:

  • dispatchTouchEvent。负责事件的分发,ViewGroup和View都有。
  • onInterceptTouchEvent。负责事件的拦截。只有ViewGroup有,返回true,代表该ViewGroup想要拦截该事件不流转给child,child可以调用requestDisallowInterceptTouchEvent(true)来请求parent跳过拦截方法。
  • onTouchEvent。负责事件的消费处理。返回true,代表该View消费该事件。

简单的分发流程图如下:
事件分发简易流程图

简单来说,首先ViewGroup会在dispatchTouchEvent中接收到事件,其中会调用onInterceptTouchEvent方法判断是否需要拦截该事件,如果不拦截,则依次按照一定的顺序遍历child列表,根据手指触摸的坐标、child的可见性等等条件筛选,调用child的dispatchTouchEvent进行下一级的事件分发,如果child是ViewGroup,则会将上面的流程在走一遍,如果是View,则在ViewdispatchTouchEvent中会调用onTouchEvent方法进行事件的消费,如果child返回true代表消费该事件,如果返回false则代表不消费,就会将该事件交给本ViewGrouponTouchEvent中进行消费。

如果以上流程中,ViewGroupView都不选择消费,就最后交给ActivityonTouchEvent方法进行消费的判断:

Activity.java
public boolean dispatchTouchEvent(MotionEvent ev) {
    if (ev.getAction() == MotionEvent.ACTION_DOWN) {
        onUserInteraction();
    }
    if (getWindow().superDispatchTouchEvent(ev)) {
        return true;
    }
    return onTouchEvent(ev);
}

4. 事件分发的结束

不管是哪个ViewGroupView选择了消费还是Activity选择是否消费,最后执行完还是回到ViewPostImeInputStage里进行后续的处理。

ViwRootImpl.java
final class ViewPostImeInputStage extends InputStage {
    private int processPointerEvent(QueuedInputEvent q) {
        final MotionEvent event = (MotionEvent)q.mEvent;
        //handled代表该事件是否被消费了
        handled = handled || mView.dispatchPointerEvent(event);
        ...
        //如果被消费,则返回的状态为FINISH_HANDLED,否则是FORWARD,继续下一层的分发处理
        return handled ? FINISH_HANDLED : FORWARD;
    }
}

返回到父类的deliver方法,走到apply里:

ViwRootImpl.java
abstract class InputStage {
    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);
        }
    }
    
    protected void apply(QueuedInputEvent q, int result) {
        if (result == FORWARD) {
            forward(q);
        } else if (result == FINISH_HANDLED) {
            finish(q, true);
        } else if (result == FINISH_NOT_HANDLED) {
            finish(q, false);
        } else {
            throw new IllegalArgumentException("Invalid result: " + result);
        }
    }
    
    protected void finish(QueuedInputEvent q, boolean handled) {
        q.mFlags |= QueuedInputEvent.FLAG_FINISHED;
        if (handled) {
            q.mFlags |= QueuedInputEvent.FLAG_FINISHED_HANDLED;
        }
        forward(q);
    }
    
    protected void forward(QueuedInputEvent q) {
        onDeliverToNext(q);
    }
    
    protected void onDeliverToNext(QueuedInputEvent q) {
        ...
        if (mNext != null) {
            mNext.deliver(q);
        } else {
            finishInputEvent(q);
        }   
    }
}

如果选择了消费,则会走到finish方法,并往QueuedInputEvent里添加FLAG_FINISHEDFLAG_FINISHED_HANDLED标志位,前者标志位代表事件分发流程的结束,后者标志位表示事件被消费。最后走到了finishInputEvent方法:

ViewRootImpl.java
private void finishInputEvent(QueuedInputEvent q) {
    if (q.mReceiver != null) {
        boolean handled = (q.mFlags & QueuedInputEvent.FLAG_FINISHED_HANDLED) != 0;
        boolean modified = (q.mFlags & QueuedInputEvent.FLAG_MODIFIED_FOR_COMPATIBILITY) != 0;
        if (modified) {
            ...
        } else {
            q.mReceiver.finishInputEvent(q.mEvent, handled);
        }
    } else {
        q.mEvent.recycleIfNeededAfterDispatch();
    }

    recycleQueuedInputEvent(q);
}
InputEventReceiver.java
public final void finishInputEvent(InputEvent event, boolean handled) {
   ...
    if (mReceiverPtr == 0) {
        Log.w(TAG, "Attempted to finish an input event but the input event " + "receiver has already been disposed.");
    } else {
        int index = mSeqMap.indexOfKey(event.getSequenceNumber());
        if (index < 0) {
            Log.w(TAG, "Attempted to finish an input event that is not in progress.");
        } else {
            int seq = mSeqMap.valueAt(index);
            mSeqMap.removeAt(index);
            nativeFinishInputEvent(mReceiverPtr, seq, handled);
        }
    }
    event.recycleIfNeededAfterDispatch();
}

接着就会调用native方法nativeFinishInputEvent来通知native层事件分发已结束。

android_view_InputEventReceiver.cpp
static void nativeFinishInputEvent(JNIEnv* env, jclass clazz, jlong receiverPtr, jint seq, jboolean handled) {
    sp<NativeInputEventReceiver> receiver =
            reinterpret_cast<NativeInputEventReceiver*>(receiverPtr);
    status_t status = receiver->finishInputEvent(seq, handled);
    ...
}

status_t NativeInputEventReceiver::finishInputEvent(uint32_t seq, bool handled) {
    ...
    Finish finish{
            .seq = seq,
            .handled = handled,
    };
    mOutboundQueue.push_back(finish);
    return processOutboundEvents();
}

NativeInputEventReceiver会将一个Finish事件插入mOutboundQueue中,

android_view_InputEventReceiver.cpp
status_t NativeInputEventReceiver::processOutboundEvents() {
    while (!mOutboundQueue.empty()) {
        OutboundEvent& outbound = *mOutboundQueue.begin();
        status_t status;
        //判断是否是Finish事件
        if (std::holds_alternative<Finish>(outbound)) {
            const Finish& finish = std::get<Finish>(outbound);
            status = mInputConsumer.sendFinishedSignal(finish.seq, finish.handled);
        }
        ...
    }

    setFdEvents(ALOOPER_EVENT_INPUT);
    return OK;
}

接下来就是调用mInputConsumersendFinishedSignal方法,内部还是通过InputChannelsendMessage方法,依靠socketpair方式将事件结束的信号发送给服务端SystemServer进程,完成跨进程通信:

InputTransport.cpp
status_t InputConsumer::sendUnchainedFinishedSignal(uint32_t seq, bool handled) {
    InputMessage msg;
    msg.header.type = InputMessage::Type::FINISHED;
    msg.header.seq = seq;
    msg.body.finished.handled = handled;
    msg.body.finished.consumeTime = getConsumeTime(seq);
    status_t result = mChannel->sendMessage(&msg);
    if (result == OK) {
        popConsumeTime(seq);
    }
    return result;
}

SystemServer端接收该消息的回调函数是:

InputDispatcher.cpp
int InputDispatcher::handleReceiveCallback(int events, sp<IBinder> connectionToken) {
    ...
    bool notify;
    if (!(events & (ALOOPER_EVENT_ERROR | ALOOPER_EVENT_HANGUP))) {
        ...
        for (;;) {
            //从inputPublisher中取出客户端发来的事件
            Result<InputPublisher::ConsumerResponse> result =
                    connection->inputPublisher.receiveConsumerResponse();
            if (!result.ok()) {
                status = result.error().code();
                break;
            }
            //如果是Finished事件
            if (std::holds_alternative<InputPublisher::Finished>(*result)) {
                const InputPublisher::Finished& finish =
                        std::get<InputPublisher::Finished>(*result);
                finishDispatchCycleLocked(currentTime, connection, finish.seq, finish.handled,
                                          finish.consumeTime);
            }
            ...
        }
        ...
    }
    ...
}

void InputDispatcher::finishDispatchCycleLocked(nsecs_t currentTime,
                                                const std::shared_ptr<Connection>& connection,
                                                uint32_t seq, bool handled, nsecs_t consumeTime) {
    ...
    auto command = [this, currentTime, connection, seq, handled, consumeTime]() REQUIRES(mLock) {
        doDispatchCycleFinishedCommand(currentTime, connection, seq, handled, consumeTime);
    };
    postCommandLocked(std::move(command));
}

void InputDispatcher::doDispatchCycleFinishedCommand(nsecs_t finishTime,
                                                     const std::shared_ptr<Connection>& connection,
                                                     uint32_t seq, bool handled,
                                                     nsecs_t consumeTime) {
    //从waitQueue中取出对应序列的事件,如果没找到,则直接返回,流程结束
    std::deque<DispatchEntry*>::iterator dispatchEntryIt = connection->findWaitQueueEntry(seq);
    if (dispatchEntryIt == connection->waitQueue.end()) {
        return;
    }
    ...
    dispatchEntryIt = connection->findWaitQueueEntry(seq);
    //从waitQueue中找到了对应序列的事件
    if (dispatchEntryIt != connection->waitQueue.end()) {
        dispatchEntry = *dispatchEntryIt;
        //从待结束队列waitQueue中删除该事件
        connection->waitQueue.erase(dispatchEntryIt);
        const sp<IBinder>& connectionToken = connection->inputChannel->getConnectionToken();
        //从ANR追踪器中删除该事件
        mAnrTracker.erase(dispatchEntry->timeoutTime, connectionToken);
        ...
    }
    
    //开始下一个事件分发的流程
    startDispatchCycleLocked(now(), connection);
}

waitingQueue是专门用来保存将事件分发给客户端并等待客户端回复的队列。在waitingQueue中找到该事件后,就从该队列中删除掉该事件,并开始下一个待分发事件的分发流程。

就此,一个事件的分发流程梳理完成了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值