美文网首页
Touch事件如何传递到Activity

Touch事件如何传递到Activity

作者: BrotherTree | 来源:发表于2019-03-06 16:17 被阅读0次

    关于Android的事件分发,我们都了解在Activity中的dispatchTouchEvent方法是分发流程的开始,那么一切的起源又在哪里呢?
    我们可以在onTouch事件中,插入语句:

    Thread.dumpStack();
    

    可以得到下面的调用栈信息:

    Stack trace
    at java.lang.Thread.dumpStack(Thread.java:1348)
    at com.example.android.MainActivity$1.onTouch(MainActivity.java:54)
    at android.view.View.dispatchTouchEvent(View.java:11823)
    at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3000)
    at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2616)
    at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3000)
    at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2616)
    at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3000)
    at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2616)
    at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3000)
    at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2616)
    at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3000)
    at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2616)
    at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3000)
    at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2616)
    at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3000)
    at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2616)
    at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3000)
    at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2616)
    at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3000)
    at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2616)
    at com.android.internal.policy.DecorView.superDispatchTouchEvent(DecorView.java:573)
    at com.android.internal.policy.PhoneWindow.superDispatchTouchEvent(PhoneWindow.java:1977)
    at android.app.Activity.dispatchTouchEvent(Activity.java:3440)
    at android.support.v7.view.WindowCallbackWrapper.dispatchTouchEvent(WindowCallbackWrapper.java:69)
    at com.android.internal.policy.DecorView.dispatchTouchEvent(DecorView.java:529)
    at android.view.View.dispatchPointerEvent(View.java:12085)
    at android.view.ViewRootImpl$ViewPostImeInputStage.processPointerEvent(ViewRootImpl.java:4986)
    at android.view.ViewRootImpl$ViewPostImeInputStage.onProcess(ViewRootImpl.java:4781)
    at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:4279)
    at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:4349)
    at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:4307)
    at android.view.ViewRootImpl$AsyncInputStage.forward(ViewRootImpl.java:4449)
    at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:4315)
    at android.view.ViewRootImpl$AsyncInputStage.apply(ViewRootImpl.java:4506)
    at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:4279)
    at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:4349)
    at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:4307)
    at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:4315)
    at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:4279)
    at android.view.ViewRootImpl.deliverInputEvent(ViewRootImpl.java:6874)
    at android.view.ViewRootImpl.doProcessInputEvents(ViewRootImpl.java:6848)
    at android.view.ViewRootImpl.enqueueInputEvent(ViewRootImpl.java:6803)
    at android.view.ViewRootImpl$WindowInputEventReceiver.onInputEvent(ViewRootImpl.java:6986)
    at android.view.InputEventReceiver.dispatchInputEvent(InputEventReceiver.java:196)
    at android.os.MessageQueue.nativePollOnce(Native Method)
    at android.os.MessageQueue.next(MessageQueue.java:332)
    at android.os.Looper.loop(Looper.java:150)
    at android.app.ActivityThread.main(ActivityThread.java:6831)
    at java.lang.reflect.Method.invoke(Native Method)
    at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:438)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:825)
    

    下面我们一步步分析调用栈信息

    at android.app.ActivityThread.main(ActivityThread.java:6831)
    at java.lang.reflect.Method.invoke(Native Method)
    at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:438)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:825)
    

    ZygoteInit初始化当前APP的进程,方法调用顺序:
    ZygoteInit.main ->
    ZygoteInit.forkSystemServer ->
    ZygoteInit.handleSystemServerProcess ->
    ZygoteInit.zygoteInit ->
    RuntimeInit.applicationInit ->
    RuntimeInit.findStaticMain ->
    new MethodAndArgsCaller ->
    MethodAndArgsCaller.run
    其中MethodAndArgsCaller是RuntimeInit的内部类,利用反射invoke获取到了当前应用程序的ActivityThread,进程中所有对Activity的操作都是调用ActivityThread中的相关方法。

    at android.view.InputEventReceiver.dispatchInputEvent(InputEventReceiver.java:196)
    at android.os.MessageQueue.nativePollOnce(Native Method)
    at android.os.MessageQueue.next(MessageQueue.java:332)
    at android.os.Looper.loop(Looper.java:150)
    

    Looper.loop方法是在ActivityThread的main方法中被调用,Android消息机制采用的是Handler机制,通过将消息封装到Message中,再发送到消息所在Handler对应的MessageQueue中,且Looper不断调用MessageQueue的next()方法进行消息的处理。
    以根据上面的调用信息,当我们触摸屏幕时,nativePollOnce()将会收到消息,并且将事件发送给InputEventReceiver的dispatchInputEvent()方法,此时FrameWork层便开始了Touch事件的传递。

    下面看一下InputEventReceiver类

    /**
     * Provides a low-level mechanism for an application to receive input events.
     * @hide
     */
    public abstract class InputEventReceiver {
        ...
        private static native long   nativeInit(WeakReference<InputEventReceiver> receiver,
                InputChannel inputChannel, MessageQueue messageQueue);
        private static native void nativeDispose(long receiverPtr);
    
        public InputEventReceiver(InputChannel inputChannel, Looper looper) {
            mReceiverPtr = nativeInit(new WeakReference<InputEventReceiver>(this),
                    inputChannel, mMessageQueue);
        }
    
        // Called from native code.
        @SuppressWarnings("unused")
        private void dispatchInputEvent(int seq, InputEvent event, int displayId) {
            mSeqMap.put(event.getSequenceNumber(), seq);
            onInputEvent(event, displayId);
        }
    }
    

    通过注释可知,InputEventReceiver提供了应用程序接受输入事件的低级机制。
    在其构造方法中,可以看到有native方法nativeInit的调用,这里系统 native 层就会将这个InputEventReceiver实例记录下来,每当有事件到达时就会通过inputChannel管道派发到这个实例上,当然还有注销的方法:dipose()。
    dispatchInputEvent方法上被注释了Called from native code,说明这个是从native层调用过来的,根据我们看到的整个调用栈信息可知,
    nativePollOnce方法内部应该就有对dispatchInputEvent的调用。

    final class WindowInputEventReceiver extends InputEventReceiver {
        public WindowInputEventReceiver(InputChannel inputChannel, Looper looper) {
               ...
        }
    
        @Override
        public void onInputEvent(InputEvent event, int displayId) {
            enqueueInputEvent(event, this, 0, true);
        }
    
        @Override
        public void onBatchedInputEventPending() {
            ...
        }
    
        @Override
        public void dispose() {
            ...
        }
    }
    

    InputEventReceiver类是一个抽象类,根据栈信息,可以看到其子类WindowInputEventReceiver重写了onInputEvent方法,WindowInputEventReceiver是ViewRootImpl的内部类。

    at android.view.ViewRootImpl.deliverInputEvent(ViewRootImpl.java:6874)
    at android.view.ViewRootImpl.doProcessInputEvents(ViewRootImpl.java:6848)
    at android.view.ViewRootImpl.enqueueInputEvent(ViewRootImpl.java:6803)
    at android.view.ViewRootImpl$WindowInputEventReceiver.onInputEvent(ViewRootImpl.java:6986)
    

    onInputEvent方法调用了ViewRootImpl类的enqueueInputEvent方法。且该方法第三个参数为true,表示该事件需要立即处理

    void enqueueInputEvent(InputEvent event,
                InputEventReceiver receiver, int flags, boolean processImmediately) {
        adjustInputEventForCompatibility(event);
        //将event事件,receiver,flags包装成一个QueuedInputEvent
        //QueuedInputEvent表示一个在队列中等待处理的输入事件,这个类有个next属性可以指向下一个事件
        QueuedInputEvent q = obtainQueuedInputEvent(event, receiver, flags);
        //获得等待队列的最后一个输入事件(Pending的意思是等待的,Tail的意思是尾部)
        QueuedInputEvent last = mPendingInputEventTail;
        //下面的意思就是将事件加入到队列中
        if (last == null) {
            //如果没有最后一个,就说明队列是空的,那么第一个是该事件,最后一个也是该事件
            mPendingInputEventHead = q;
            mPendingInputEventTail = q;
        } else {
            //如果有最后一个,那么就将该事件设置成最后一个
            last.mNext = q;
            mPendingInputEventTail = q;
        }
        //队列数量加1
        mPendingInputEventCount += 1;
        //事件跟踪机制。。。不需要管
        Trace.traceCounter(Trace.TRACE_TAG_INPUT, mPendingInputEventQueueLengthCounterName,
                    mPendingInputEventCount);
        //如果事件需要立即处理,则执行doProcessInputEvents(),
        //WindowInputEventReceiver中enqueueInputEvent(event, this, 0, true);传入的是true
        if (processImmediately) {
            doProcessInputEvents();
        } else {
            scheduleProcessInputEvents();
        }
    }
    

    由于入参processImmediately的值为true,则会继续执行到doProcessInputEvents方法。

    void doProcessInputEvents() {
        // 处理队列中所有的输入事件
        while (mPendingInputEventHead != null) {
            //下面这段代码是取出事件队列中的第一个,若有第二个,将其置为第一个
            QueuedInputEvent q = mPendingInputEventHead;
            mPendingInputEventHead = q.mNext;
            if (mPendingInputEventHead == null) {
                mPendingInputEventTail = null;
            }
            q.mNext = null;
            //队列数量减1
            mPendingInputEventCount -= 1;
            //跟踪事件,不需要管
            Trace.traceCounter(Trace.TRACE_TAG_INPUT, mPendingInputEventQueueLengthCounterName,
                        mPendingInputEventCount);
            //下面的代码是获得当前事件的发生时间,以及此事件与上一个事件间隔间隔时间
            //通过Choreographer,协调动画、输入和绘图的时间
            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);
        }
        // 处理完了所有的输入事件,将处理事件等待标记设为false
        if (mProcessInputEventsScheduled) {
            mProcessInputEventsScheduled = false;
            mHandler.removeMessages(MSG_PROCESS_INPUT_EVENTS);
        }
    }
    

    接下来走到了deliverInputEvent方法

        private void deliverInputEvent(QueuedInputEvent q) {
            //跟踪机制,不用管
            Trace.asyncTraceBegin(Trace.TRACE_TAG_VIEW, "deliverInputEvent",
                    q.mEvent.getSequenceNumber());
            //一致性验证,不用管,一致性验证就是比如说判断ACTION_DOWN和ACTION_UP是否成对出现
            if (mInputEventConsistencyVerifier != null) {
                mInputEventConsistencyVerifier.onInputEvent(q.mEvent, 0);
            }
    
            InputStage stage;
            if (q.shouldSendToSynthesizer()) {
                stage = mSyntheticInputStage;
            } else {
                stage = q.shouldSkipIme() ? mFirstPostImeInputStage : mFirstInputStage;
            }
    
            if (q.mEvent instanceof KeyEvent) {
                mUnhandledKeyManager.preDispatch((KeyEvent) q.mEvent);
            }
    
            if (stage != null) {
                handleWindowFocusChanged();
                stage.deliver(q);
            } else {
                finishInputEvent(q);
            }
        }
    

    这里出现了InputStage,它是处理输入事件的一个阶段,可以将事件完成或者转送到下一个阶段。
    InputStage可以分很多种,如下示例:

    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);
    

    Android在这里使用了设计模式中的责任链模式,多个InputStage连成一条链,并沿着这条链传递输入事件,直到有一个InputStage处理了该输入事件

    at android.view.ViewRootImpl$ViewPostImeInputStage.processPointerEvent(ViewRootImpl.java:4986)
    at android.view.ViewRootImpl$ViewPostImeInputStage.onProcess(ViewRootImpl.java:4781)
    at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:4279)
    at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:4349)
    at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:4307)
    at android.view.ViewRootImpl$AsyncInputStage.forward(ViewRootImpl.java:4449)
    at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:4315)
    at android.view.ViewRootImpl$AsyncInputStage.apply(ViewRootImpl.java:4506)
    at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:4279)
    at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:4349)
    at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:4307)
    at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:4315)
    at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:4279)
    

    触摸事件是由ViewPostImeInputStage处理,事件最终传递给了ViewPostImeInputStage中的onProcess()然后传递给processPointerEvent()方法

    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) {
        ...
        boolean handled = mView.dispatchPointerEvent(event);
        ...
    }
    

    终于有一个看起来熟悉的东西了,这里的mView是什么

    at android.app.Activity.dispatchTouchEvent(Activity.java:3440)
    at android.support.v7.view.WindowCallbackWrapper.dispatchTouchEvent(WindowCallbackWrapper.java:69)
    at com.android.internal.policy.DecorView.dispatchTouchEvent(DecorView.java:529)
    at android.view.View.dispatchPointerEvent(View.java:12085)
    

    我们看一下View类中的dispatchPointerEvent方法

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

    通过调用栈,可以发现实际上mView就是DecorView,接下来就是走DecorView的dispatchTouchEvent方法

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

    mWindow就是与Activity关联的PhoneWindow对象,由于DecorView是PhoneWindow创建的,并且通过setWindow()方法,DecorView对象持有了PhoneWindow对象的引用。通过getCallback()方法,就获得了Window.Callback对象,Window.Callback包含了窗口的各种回调接口,Activity就实现了该接口。根据return后的判断,当调用cb.dispatchTouchEvent(ev)时,其实调用的就是Activity中的dispatchTouchEvent()方法

    至此,Touch事件是如何传递到Activity的dispatchTouchEvent方法中的流程就全部分析完了,至于接下来的详细的事件分发流程,可以在Android事件分发机制中了解到。

    相关文章

      网友评论

          本文标题:Touch事件如何传递到Activity

          本文链接:https://www.haomeiwen.com/subject/xbhxpqtx.html