触摸事件从ViewRootImpl到DecorView的派发过程

ViewRootImpl & ViewGroup & View 触摸事件派发机制源码分析

简述

  • Activity顶层窗口接受屏幕触摸事件的准备以及对输入事件到来时候的预处理;
  • ViewGroup的事件派发机制dispatchTouchEvent()分析;
  • View自身的事件派发机制dispatchTouchEvent()分析;
  • View自身onTouchEvent()方法分析;

Activity窗口接受屏幕触摸事件的准备

一个Activity有一个PhoneWindow窗口,对应一个顶层ViewParent的实现类ViewRootImpl,窗口接受屏幕事件的准备工作是在ViewRootImpl.setView()中进行的

public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
    synchronized (this) {
        if (mView == null) {
            mView = view;
            .....
            // Schedule the first layout -before- adding to the window
            // manager, to make sure we do the relayout before receiving
            // any other events from the system.
            // 这里先去向主线程发个消息稍后就去发起视图树的测量,布局,绘制显示工作,这样下面的操作完成后,视图窗口显示出来就可以马上接受各种输入事件了
            requestLayout();
            // 一般没有特别设置该窗口不能接受输入事件设置,这里if==true
            if ((mWindowAttributes.inputFeatures
                    & WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
                // 初始化设置一个当前窗口可以联通接受系统输入事件的的通道
                mInputChannel = new InputChannel();
            }
            try {
                mOrigWindowType = mWindowAttributes.type;
                mAttachInfo.mRecomputeGlobalAttributes = true;
                collectViewAttributes();
                // 建立当前视图窗口与系统WindowManagerService服务的关联,并传入刚才创建的mInputChannel
                // 会在WindowManagerService服务进程为该APP窗口生成两个InputQueue,其中一个会调用InputQueue.transferTo()返回到当前APP进程窗口;另外一个保留在WindowManagerService为当前APP窗口创建的WindowState中
                res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
                        getHostVisibility(), mDisplay.getDisplayId(),
                        mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
                        mAttachInfo.mOutsets, mInputChannel);
            } catch (RemoteException e) {
                mAdded = false;
                mView = null;
                mAttachInfo.mRootView = null;
                mInputChannel = null;
                mFallbackEventHandler.setView(null);
                unscheduleTraversals();
                setAccessibilityFocus(null, null);
                throw new RuntimeException("Adding window failed", e);
            } finally {
                if (restore) {
                    attrs.restore();
                }
            }
            .....
            // 很明显,view是PhoneWindow的内部类DecorView对象,而DecorView extends FrameLayout implements RootViewSurfaceTaker 
            // 所以这里if==ture
            if (view instanceof RootViewSurfaceTaker) {
                // 创建InputQueue的create和destroy的通知对象,这里DecroView.willYouTakeTheInputQueue()一般为null
                mInputQueueCallback =
                    ((RootViewSurfaceTaker)view).willYouTakeTheInputQueue();
            }
            // 从上面知道,一般没有特别设置该窗口不能接受输入事件设置,这里mInputChannel!=null已经生成
            if (mInputChannel != null) {
                // 一般情况DecroView.willYouTakeTheInputQueue()为null,所以这里if==false
                if (mInputQueueCallback != null) {
                    mInputQueue = new InputQueue();
                    mInputQueueCallback.onInputQueueCreated(mInputQueue);
                }
                // 创建一个与当前窗口已经生成的InputChannel相关的接受输入事件的处理对象
                mInputEventReceiver = new WindowInputEventReceiver(mInputChannel,
                        Looper.myLooper());
            }
            view.assignParent(this);
            mAddedTouchMode = (res & WindowManagerGlobal.ADD_FLAG_IN_TOUCH_MODE) != 0;
            mAppVisible = (res & WindowManagerGlobal.ADD_FLAG_APP_VISIBLE) != 0;

            if (mAccessibilityManager.isEnabled()) {
                mAccessibilityInteractionConnectionManager.ensureConnection();
            }

            if (view.getImportantForAccessibility() == View.IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
                view.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
            }

            // 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;
            mPendingInputEventQueueLengthCounterName = "aq:pending:" + counterSuffix;

        }

    }

}

从上面看出,在窗口初始化即ViewRootImpl.setView()中,会建立当前视图窗口体系与系WindowManagerService服务的关联,并在系统WindowManagerService服务为该窗口生成两个InputChannel输入事件通道,一个转移到当前顶层ViewParent即ViewRootImpl中,并在ViewRootImpl生成一个与输入事件通道关联的事件处理WindowInputEventReceiver内部类对象mInputEventReceiver:

final class WindowInputEventReceiver extends InputEventReceiver {
    public WindowInputEventReceiver(InputChannel inputChannel, Looper looper) {
        super(inputChannel, looper);
    }

    // 重写了父类onInputEvent,调用enqueueInputEvent实际处理native层返回的InputEvent输入事件
    @Override
    public void onInputEvent(InputEvent event) {
        enqueueInputEvent(event, this, 0, true);
    }

    @Override
    public void onBatchedInputEventPending() {
        if (mUnbufferedInputDispatch) {
            super.onBatchedInputEventPending();
        } else {
            scheduleConsumeBatchedInput();
        }
    }

    @Override
    public void dispose() {
        unscheduleConsumeBatchedInput();
        super.dispose();
    }
}

// 看下WindowInputEventReceiver父类
public abstract class InputEventReceiver {

    ....

    // Called from native code.
    @SuppressWarnings("unused")
    // 当输入事件到来时该方法由native层代码发起调用
    private void dispatchInputEvent(int seq, InputEvent event) {
        mSeqMap.put(event.getSequenceNumber(), seq);
        onInputEvent(event);
    }
    /**
     * Called when an input event is received.
     * The recipient should process the input event and then call {@link #finishInputEvent}
     * to indicate whether the event was handled.  No new input events will be received
     * until {@link #finishInputEvent} is called.
     *
     * @param event The input event that was received.
     */
    public void onInputEvent(InputEvent event) {
        finishInputEvent(event, false);
    }
    ....

}

至此可以看到,一个屏幕输入事件返回处理在当前视图窗口WindowInputEventReceiver内部类的onInputEvent(InputEvent event)中,随即调用了外部类ViewRootImpl.enqueueInputEvent(event, this, 0, true),需要注意的是这里的参数flags==0

void enqueueInputEvent(InputEvent event,
        InputEventReceiver receiver, int flags, boolean processImmediately) {
    adjustInputEventForCompatibility(event);
    QueuedInputEvent q = obtainQueuedInputEvent(event, receiver, flags);

    // Always enqueue the input event in order, regardless of its time stamp.
    // We do this because the application or the IME may inject key events
    // in response to touch events and we want to ensure that the injected keys
    // are processed in the order they were received and we cannot trust that
    // the time stamp of injected events are monotonic.
    QueuedInputEvent last = mPendingInputEventTail;
    if (last == null) {
        mPendingInputEventHead = q;
        mPendingInputEventTail = q;
    } else {
        last.mNext = q;
        mPendingInputEventTail = q;
    }
    mPendingInputEventCount += 1;
    Trace.traceCounter(Trace.TRACE_TAG_INPUT, mPendingInputEventQueueLengthCounterName,
            mPendingInputEventCount);

    if (processImmediately) {
        doProcessInputEvents();
    } else {
        scheduleProcessInputEvents();
    }
}

先是获取一个指向当前事件的输入事件队列QueuedInputEvent对象,然后根据情况赋值ViewRootImpl成员变量mPendingInputEventHead或者追加到mPendingInputEventTail的mNext尾部,随即调用doProcessInputEvents()

void doProcessInputEvents() {
    // Deliver all pending input events in the queue.
    while (mPendingInputEventHead != null) {
        QueuedInputEvent q = mPendingInputEventHead;
        mPendingInputEventHead = q.mNext;
        if (mPendingInputEventHead == null) {
            mPendingInputEventTail = null;
        }
        q.mNext = null;

        mPendingInputEventCount -= 1;
        Trace.traceCounter(Trace.TRACE_TAG_INPUT, mPendingInputEventQueueLengthCounterName,
                mPendingInputEventCount);

        long eventTime = q.mEvent.getEventTimeNano();
        long oldestEventTime = eventTime;
        if (q.mEvent instanceof MotionEvent) {
            MotionEvent me = (MotionEvent)q.mEvent;
            if (me.getHistorySize() > 0) {
                oldestEventTime = me.getHistoricalEventTimeNano(0);
            }
        }
        mChoreographer.mFrameInfo.updateInputEventTime(eventTime, oldestEventTime);

        deliverInputEvent(q);
    }

    // We are done processing all input events that we can process right now
    // so we can clear the pending flag immediately.
    if (mProcessInputEventsScheduled) {
        mProcessInputEventsScheduled = false;
        mHandler.removeMessages(MSG_PROCESS_INPUT_EVENTS);
    }
}

只要mPendingInputEventHead!=null即当前待处理事件队列还有事件需要去被处理掉,就一直循环调用deliverInputEvent()

private void deliverInputEvent(QueuedInputEvent q) {
    Trace.asyncTraceBegin(Trace.TRACE_TAG_VIEW, "deliverInputEvent",
            q.mEvent.getSequenceNumber());
    if (mInputEventConsistencyVerifier != null) {
        mInputEventConsistencyVerifier.onInputEvent(q.mEvent, 0);
    }

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

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

这里分别有两个判断,q.shouldSendToSynthesizer()和q.shouldSkipIme(),上面WindowInputEventReceiver拿到native返回的一个输入事件对象时候,调用的ViewRootImpl.enqueueInputEvent(event, this, 0, true),标记了输入事件的QueuedInputEvent对象至此为止falg==0,所以,这里一般情况调用在setView()中生成的NativePreImeInputStage mFirstInputStage对象接着去处理.从setView()方法中可知,一共生成了7个InputStage的子类对象依次接龙按事件类型对应去处理,入口是NativePreImeInputStage该子类对象,NativePreImeInputStage的顶层父类当然也是InputStage:

abstract class InputStage {
    private final InputStage mNext;

    protected static final int FORWARD = 0;
    protected static final int FINISH_HANDLED = 1;
    protected static final int FINISH_NOT_HANDLED = 2;

    /**
     * Creates an input stage.
     * @param next The next stage to which events should be forwarded.
     */
    public InputStage(InputStage next) {
        mNext = next;
    }

    /**
     * Delivers an event to be processed.
     */
    public final void deliver(QueuedInputEvent q) {
        if ((q.mFlags & QueuedInputEvent.FLAG_FINISHED) != 0) {
            forward(q);
        } else if (shouldDropInputEvent(q)) {
            finish(q, false);
        } else {
            apply(q, onProcess(q));
        }
    }

    /**
     * Marks the the input event as finished then forwards it to the next stage.
     */
    protected void finish(QueuedInputEvent q, boolean handled) {
        q.mFlags |= QueuedInputEvent.FLAG_FINISHED;
        if (handled) {
            q.mFlags |= QueuedInputEvent.FLAG_FINISHED_HANDLED;
        }
        forward(q);
    }

    /**
     * Forwards the event to the next stage.
     */
    protected void forward(QueuedInputEvent q) {
        onDeliverToNext(q);
    }

    /**
     * Applies a result code from {@link #onProcess} to the specified event.
     */
    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);
        }
    }

    /**
     * Called when an event is ready to be processed.
     * @return A result code indicating how the event was handled.
     */
    protected int onProcess(QueuedInputEvent q) {
        return FORWARD;
    }

    /**
     * Called when an event is being delivered to the next stage.
     */
    protected void onDeliverToNext(QueuedInputEvent q) {
        if (DEBUG_INPUT_STAGES) {
            Log.v(TAG, "Done with " + getClass().getSimpleName() + ". " + q);
        }
        if (mNext != null) {
            mNext.deliver(q);
        } else {
            finishInputEvent(q);
        }
    }   
    ....
}
  • 从构造就可以看出,每个生成的InputStage对象都会有一个成员变量mNext指向下一个处理事件的InputStage对象;
  • deliver()方法先判断该事件对象是否已经处理完成或者需要抛弃掉,都不满足则调用onProcess()处理该事件对象,处理完成后返回处理结果给apply()方法后续工作,根据onProcess()返回处理结果是否把事件传递给其mNext指向的下一个InputStage去处理;
  • 当然具体处理是在子类的onProcess()中实现的了
final class NativePreImeInputStage extends AsyncInputStage
        implements InputQueue.FinishedInputEventCallback {
    public NativePreImeInputStage(InputStage next, String traceCounter) {
        super(next, traceCounter);
    }

    @Override
    protected int onProcess(QueuedInputEvent q) {
        if (mInputQueue != null && q.mEvent instanceof KeyEvent) {
            mInputQueue.sendInputEvent(q.mEvent, q, true, this);
            return DEFER;
        }
        return FORWARD;
    }

    @Override
    public void onFinishedInputEvent(Object token, boolean handled) {
        QueuedInputEvent q = (QueuedInputEvent)token;
        if (handled) {
            finish(q, true);
            return;
        }
        forward(q);
    }
}

对于屏幕触摸事件,这里NativePreImeInputStage的onProcess()返回FORWARD,即交给其mNext即ViewPreImeInputStage去接龙处理

final class ViewPreImeInputStage extends InputStage {
    public ViewPreImeInputStage(InputStage next) {
        super(next);
    }

    @Override
    protected int onProcess(QueuedInputEvent q) {
        if (q.mEvent instanceof KeyEvent) {
            return processKeyEvent(q);
        }
        return FORWARD;
    }

    private int processKeyEvent(QueuedInputEvent q) {
        final KeyEvent event = (KeyEvent)q.mEvent;
        if (mView.dispatchKeyEventPreIme(event)) {
            return FINISH_HANDLED;
        }
        return FORWARD;
    }

对于触摸事件ViewPreImeInputStage.onProcess()同样返回FORWARD,交给其其mNext即ViewPreImeInputStage去接龙处理ImeInputStage去处理

/**
 * Delivers input events to the ime.
 * Does not support pointer events.
 */
final class ImeInputStage extends AsyncInputStage
        implements InputMethodManager.FinishedInputEventCallback {
    public ImeInputStage(InputStage next, String traceCounter) {
        super(next, traceCounter);
    }

    @Override
    protected int onProcess(QueuedInputEvent q) {
        if (mLastWasImTarget && !isInLocalFocusMode()) {
            InputMethodManager imm = InputMethodManager.peekInstance();
            if (imm != null) {
                final InputEvent event = q.mEvent;
                if (DEBUG_IMF) Log.v(TAG, "Sending input event to IME: " + event);
                int result = imm.dispatchInputEvent(event, q, this, mHandler);
                if (result == InputMethodManager.DISPATCH_HANDLED) {
                    return FINISH_HANDLED;
                } else if (result == InputMethodManager.DISPATCH_NOT_HANDLED) {
                    // The IME could not handle it, so skip along to the next InputStage
                    return FORWARD;
                } else {
                    return DEFER; // callback will be invoked later
                }
            }
        }
        return FORWARD;
    }

    @Override
    public void onFinishedInputEvent(Object token, boolean handled) {
        QueuedInputEvent q = (QueuedInputEvent)token;
        if (handled) {
            finish(q, true);
            return;
        }
        forward(q);
    }
}

很明显这里是处理输入法事件的,对于一般触摸事件同样返回FORWARD交给其mNext 即EarlyPostImeInputStage去处理,查看EarlyPostImeInputStage源码可知其对于触摸事件onProcess()返回FORWARD交给其mNext 即NativePostImeInputStage去处理,而NativePostImeInputStage.onProcess()同样返回FORWARD交给其mNext 即ViewPostImeInputStage去处理,看出ViewPostImeInputStage.onProcess():

final class ViewPostImeInputStage extends InputStage {

    ....

    @Override
    protected int onProcess(QueuedInputEvent q) {
        if (q.mEvent instanceof KeyEvent) {
            return processKeyEvent(q);
        } else {
            // If delivering a new non-key event, make sure the window is
            // now allowed to start updating.
            handleDispatchWindowAnimationStopped();
            final int source = q.mEvent.getSource();
            // Whoops here!!
            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;

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

    ....

}

对于触摸事件,这里的if ((source & InputDevice.SOURCE_CLASS_POINTER) != 0)为ture,所以调用processPointerEvent()去处理,在processPointerEvent()中直接调用mView.dispatchPointerEvent(event),而mView即窗口的顶层视图DecroView;

至此,终于看到ViewRootImpl对输入事件的准备工作以及经过一系列处理把触摸事件交由顶层视图DecroView的过程,DecroView和其父类FrameLayout,ViewGroup均没有重写此方法,故在View.dispatchPointerEvent()中:

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

很明显,这里调用了dispatchTouchEvent()去处理,而DecorView重新了该方法:

    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        // 在Activity.attach()中已经把自己设置赋值到DecroView的外部类Window的Callback mCallback成员变量
        // 且在PhoneWindow生成DecroView对象的时候传入的mFeatureId=-1
        final Callback cb = getCallback();
        return cb != null && !isDestroyed() && mFeatureId < 0 ? cb.dispatchTouchEvent(ev)
                : super.dispatchTouchEvent(ev);
    }

所以这里,调用了Acitivity.dispatchTouchEvent()去处理

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

又让PhoneWindow.superDispatchTouchEvent()处理:

@Override
public boolean superDispatchTouchEvent(MotionEvent event) {
    return mDecor.superDispatchTouchEvent(event);
}

又让DecroView.superDispatchTouchEvent()处理:

    public boolean superDispatchTouchEvent(MotionEvent event) {
        return super.dispatchTouchEvent(event);
    }

DecroView继承之FrameLayout,FrameLayout继承之ViewGroup,所以事件终于到了ViewGroup的dispatchTouchEvent()中去处理了!

到这里,事件终于到了ViewGroup.dispatchTouchEvent()了.



作者:Nvsleep
链接:ViewRootImpl & ViewGroup & View 触摸事件派发机制源码分析 - 简书
来源:简书

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值