美文网首页
事件处理机制(一)

事件处理机制(一)

作者: 涛涛123759 | 来源:发表于2021-09-27 15:15 被阅读0次

    Android知识总结

    一、事件接收流程

    首先我们在ViewRootImpl#setView创建View的接收事件

        public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView, int userId) {
            ...
            if (inputChannel != null) {
                if (mInputQueueCallback != null) {
                    mInputQueue = new InputQueue();
                    mInputQueueCallback.onInputQueueCreated(mInputQueue);
                }
                //接收事件
                mInputEventReceiver = new WindowInputEventReceiver(inputChannel,
                        Looper.myLooper());
            }
            ...
        }
    

    然后我们看ViewRootImpl的内部类WindowInputEventReceiver

        final class WindowInputEventReceiver extends InputEventReceiver {
            public WindowInputEventReceiver(InputChannel inputChannel, Looper looper) {
                super(inputChannel, looper);
            }
    
            @Override
            public void onInputEvent(InputEvent event) {
                Trace.traceBegin(Trace.TRACE_TAG_VIEW, "processInputEventForCompatibility");
                List<InputEvent> processedEvents;
                try {
                    processedEvents =
                        mInputCompatProcessor.processInputEventForCompatibility(event);
                } finally {
                    Trace.traceEnd(Trace.TRACE_TAG_VIEW);
                }
                if (processedEvents != null) {
                    if (processedEvents.isEmpty()) {
                        // InputEvent consumed by mInputCompatProcessor
                        finishInputEvent(event, true);
                    } else {
                        for (int i = 0; i < processedEvents.size(); i++) {
                            //派发事件
                            enqueueInputEvent(
                                    processedEvents.get(i), this,
                                    QueuedInputEvent.FLAG_MODIFIED_FOR_COMPATIBILITY, true);
                        }
                    }
                } else {
                    enqueueInputEvent(event, this, 0, true);
                }
            }
    
            @Override
            public void onBatchedInputEventPending(int source) {
                // mStopped: There will be no more choreographer callbacks if we are stopped,
                // so we must consume all input immediately to prevent ANR
                final boolean unbuffered = mUnbufferedInputDispatch
                        || (source & mUnbufferedInputSource) != SOURCE_CLASS_NONE
                        || mStopped;
                if (unbuffered) {
                    if (mConsumeBatchedInputScheduled) {
                        unscheduleConsumeBatchedInput();
                    }
                    // Consume event immediately if unbuffered input dispatch has been requested.
                    consumeBatchedInputEvents(-1);
                    return;
                }
                scheduleConsumeBatchedInput();
            }
    
            @Override
            public void onFocusEvent(boolean hasFocus, boolean inTouchMode) {
                windowFocusChanged(hasFocus, inTouchMode);
            }
    
            @Override
            public void dispose() {
                unscheduleConsumeBatchedInput();
                super.dispose();
            }
        }
        WindowInputEventReceiver mInputEventReceiver;
    

    然后在onInputEvent中调用enqueueInputEvent获取事件

        void enqueueInputEvent(InputEvent event,
                InputEventReceiver receiver, int flags, boolean processImmediately) {
            QueuedInputEvent q = obtainQueuedInputEvent(event, receiver, flags);
            // 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();
            }
        }
    
    
        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);
            }
            // so we can clear the pending flag immediately.
            if (mProcessInputEventsScheduled) {
                mProcessInputEventsScheduled = false;
                //移出事件
                mHandler.removeMessages(MSG_PROCESS_INPUT_EVENTS);
            }
        }
    
        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);
                }
        }
    

    在这个方法中调用InputStage的各个方法deliver, 处理完事件后就会finishInputEvent来完成事件分发操作。

    1.1、各个InputStage

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

    构造了7个InputStage实现类,

    • NativePreImeInputStage: 主要是为了将消息放到NativeActivity中去处理, NativeActivity和普通Acitivty的功能区别不大,只是很多代码都在native层去实现,这样执行效率更高,并且NativeActivity在游戏开发中很实用。
    • ViewPreImeInputStage: 从名字中就可得知,最后会调用Acitivity的所有view的onkeyPreIme方法,这样就给View在输入法处理key事件之前先得到消息并处理的机会。
    • ImeInputStage: ImeInputStage的onProcess方法会调用InputMethodManager的dispatchInputEvent方法处理消息。
    • EarlyPostImeInputStage: 屏幕上有焦点的View会高亮显示,用来提示用户焦点所在。
    • NativePostImeInputStage: 为了让IME处理完消息后能先于普通的Activity处理消息。
    • ViewPostImeInputStage: Acitivity和view处理各种消息。
    • SyntheticInputStage: 流水线的最后一级,经过层层过滤之后,到达这里的消息已经不多了,例如手机上的虚拟按键消息。

    那么Activity和View的事件处理主要对应的InputStage是ViewPostImeInputStage。

    1.2、ViewPostImeInputStage

    调用ViewPostImeInputStage的父类InputStage#deliver方法

    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 int onProcess(QueuedInputEvent q) {
             return FORWARD;
        }
    }
    

    接下来看 ViewPostImeInputStageonProcess方法

        final class ViewPostImeInputStage extends InputStage {
            public ViewPostImeInputStage(InputStage next) {
                super(next);
            }
    
            @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;
    
            mAttachInfo.mUnbufferedDispatchRequested = false;
            mAttachInfo.mHandlingPointerEvent = true;
            //mView ==> DecorView
            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;
        }
    

    会调用到DecorView 的 dispatchTouchEvent

    二、View继承关系调用

    2.1、我们先看DecorView 的继承关系

    private final class DecorView extends FrameLayout implements RootViewSurfaceTaker{...}
    public class FrameLayout extends ViewGroup {...}
    public abstract class ViewGroup extends View implements ViewParent, ViewManager{..}
    

    2.2、事件处理

    最后回调到父类ViewdispatchPointerEvent

        public final boolean dispatchPointerEvent(MotionEvent event) {
            if (event.isTouchEvent()) {
                //分发事件
                return dispatchTouchEvent(event);
            } else {
                return dispatchGenericMotionEvent(event);
            }
        }
    

    然后再回到顶层DecorView中执行dispatchTouchEvent

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

    调用ActivitydispatchTouchEvent,先调用各个DispatchTouchEvent,最后才调用 onTouchEvent。因此最早 dispatchTouchEvent,应用最晚 onTouchEvent

        public boolean dispatchTouchEvent(MotionEvent ev) {
            if (ev.getAction() == MotionEvent.ACTION_DOWN) {
                onUserInteraction();
            }
             //getWindow()获取的是PhoneWindow,子View 处理
            if (getWindow().superDispatchTouchEvent(ev)) {
                return true;
            }
            //如果子View都不处理,自己处理事件
            return onTouchEvent(ev);
        }
    

    调用PhoneWindow#superDispatchTouchEvent

        public boolean superDispatchTouchEvent(MotionEvent event) {
          //mDecor ==> DecorView
            return mDecor.superDispatchTouchEvent(event);
        }
    

    调用DecorView#superDispatchTouchEvent

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

    调用ViewGoup#superDispatchTouchEvent

        public boolean dispatchTouchEvent(MotionEvent ev) {
            ...
        }
    
    
    private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,
            View child, int desiredPointerIdBits) {
        final boolean handled;
        ...
        // Perform any necessary transformations and dispatch.
        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);
        }
    

    最后调用View#dispatchTouchEvent

    public boolean dispatchTouchEvent(MotionEvent event) { ... }
    

    在调用 onTouchEvent处理事件

    public boolean onTouchEvent(MotionEvent event) { ... }
    
    事件种类

    具体事件怎么处理见下章

    相关文章

      网友评论

          本文标题:事件处理机制(一)

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