溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

Andriod事件分發(fā)事件怎么來的

發(fā)布時間:2023-03-15 14:17:10 來源:億速云 閱讀:101 作者:iii 欄目:開發(fā)技術(shù)

本篇內(nèi)容主要講解“Andriod事件分發(fā)事件怎么來的”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實用性強。下面就讓小編來帶大家學(xué)習(xí)“Andriod事件分發(fā)事件怎么來的”吧!

Android事件分發(fā)的事件從何而來

事件分發(fā)一直以來都是一個android知識的重點。從應(yīng)用開發(fā)角度和用戶的交互就是在處理事件。

Activity的事件分發(fā)

事件分發(fā)一般情況都會講view的分發(fā)過程,他的過程縮略起來就可以這樣表示。

public boolean diapatchTouchEvent(MotionEvent ev) {
    boolean consume = false;
    if (onInterceptTouchEvent(ev)) {
        consume = onTouchEvent(ev);
    } else {
        consume = child.dispatchTouchEvent(ev);
    }
    return consume;
}

這里就有一個問題,最早的事件是從哪里來的。根據(jù)Android的視圖模型知道最外層的view就是DecorView ,而它的外面是一個PhoneWindow。所以最初的事件就是從PhoneWindow進入了view的事件分發(fā),而PhoneWindow的事件又是Activity中來的.

//frameworks/base/core/java/android/app/Activity.java
public boolean dispatchTouchEvent(MotionEvent ev) {
        if (ev.getAction() == MotionEvent.ACTION_DOWN) {
            onUserInteraction();
        }
        if (getWindow().superDispatchTouchEvent(ev)) {//這里獲取的PhoneWindow
            return true;
        }
        return onTouchEvent(ev);
    }

那么問題又來了,activity的事件是哪里來的呢。

ViewRootImpl事件分發(fā)

熟悉Android的Window創(chuàng)建流程的話就知道ViewRootImpl是所有view的最頂層。也是ViewRootImpl在setView中實現(xiàn)了View和WindowManager之間的交互。這個方法里有一個在Window創(chuàng)建流程的時候沒有關(guān)注的InputChannel,事件真正的來源就是它,在

 public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView,
            int userId) {
        synchronized (this) {
            if (mView == null) {
                mView = view;
              .........
                InputChannel inputChannel = null;//創(chuàng)建InputChannel
                if ((mWindowAttributes.inputFeatures
                        & WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
                    inputChannel = new InputChannel();
                }
              
                    res = mWindowSession.addToDisplayAsUser(mWindow, mWindowAttributes,
                            getHostVisibility(), mDisplay.getDisplayId(), userId,
                            mInsetsController.getRequestedVisibilities(), inputChannel, mTempInsets,mTempControls, attachedFrame, sizeCompatScale);//將InputChannel傳給WMS
                if (inputChannel != null) {
                    if (mInputQueueCallback != null) {
                        mInputQueue = new InputQueue();
                        mInputQueueCallback.onInputQueueCreated(mInputQueue);
                    }
                    mInputEventReceiver = new WindowInputEventReceiver(inputChannel,
                            Looper.myLooper());//創(chuàng)建mInputEventReceiver
                }
              //這里創(chuàng)建了各種事件處理器
                // 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;
                AnimationHandler.requestAnimatorsEnabled(mAppVisible, this);
            }
        }
    }

從名字也能猜出mInputEventReceiver就是接收事件的對象了,這是一個ViewRootImpl的內(nèi)部類看下它的實現(xiàn)。

 final class WindowInputEventReceiver extends InputEventReceiver {
        public WindowInputEventReceiver(InputChannel inputChannel, Looper looper) {
            super(inputChannel, looper);
        }
        @Override
        public void onInputEvent(InputEvent event) {//通過名字就知道這應(yīng)該是事件接收的回調(diào)
            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);
            }
        }
   .......
 }

如果processedEvents不為空都是調(diào)用了enqueueInputEvent,不然就直接調(diào)用finishInputEvent。

 void enqueueInputEvent(InputEvent event,
            InputEventReceiver receiver, int flags, boolean processImmediately) {
        QueuedInputEvent q = obtainQueuedInputEvent(event, receiver, flags);
        //這里做了區(qū)分是觸摸事件還是按鍵事件
        if (event instanceof MotionEvent) {
            MotionEvent me = (MotionEvent) event;
        } else if (event instanceof KeyEvent) {
            KeyEvent ke = (KeyEvent) event;
        }
       
        QueuedInputEvent last = mPendingInputEventTail;
        if (last == null) {
            mPendingInputEventHead = q;
            mPendingInputEventTail = q;
        } else {
            last.mNext = q;
            mPendingInputEventTail = q;
        }
        mPendingInputEventCount += 1;
        if (processImmediately) {
            doProcessInputEvents();
        } else {
            scheduleProcessInputEvents();
        }
    }
    private void scheduleProcessInputEvents() {
        if (!mProcessInputEventsScheduled) {
            mProcessInputEventsScheduled = true;
            Message msg = mHandler.obtainMessage(MSG_PROCESS_INPUT_EVENTS);
            msg.setAsynchronous(true);
            mHandler.sendMessage(msg);
        }
    }
  private void handleMessageImpl(Message msg) {
            switch (msg.what) {
               case MSG_PROCESS_INPUT_EVENTS:
                    mProcessInputEventsScheduled = false;
                    doProcessInputEvents();
            }
  }

這里判斷了是否要立即消費,如果立即消費doProcessInputEvents,不然調(diào)用scheduleProcessInputEvents。而scheduleProcessInputEvents很簡單就是handle發(fā)送了一個異步消息。最后handle執(zhí)行的時候還是會調(diào)用到doProcessInputEvents。所以就來詳細看下doProcessInputEvents。

    void doProcessInputEvents() {
        // Deliver all pending input events in the queue.
        while (mPendingInputEventHead != null) {//循環(huán)獲取InputEvent并處理
            QueuedInputEvent q = mPendingInputEventHead;
            mPendingInputEventHead = q.mNext;
            if (mPendingInputEventHead == null) {
                mPendingInputEventTail = null;
            }
            q.mNext = null;
            mPendingInputEventCount -= 1;
            mViewFrameInfo.setInputEvent(mInputEventAssigner.processEvent(q.mEvent));
            deliverInputEvent(q);
        }
        //移除異步消息
        if (mProcessInputEventsScheduled) {
            mProcessInputEventsScheduled = false;
            mHandler.removeMessages(MSG_PROCESS_INPUT_EVENTS);
        }
    }

可以看到真實的處理都是deliverInputEvent來處理。

 private void deliverInputEvent(QueuedInputEvent q) {
        try {
            if (mInputEventConsistencyVerifier != null) {
            InputStage stage;//在ViewRootImpl的setView中初始化的處理器
            if (q.shouldSendToSynthesizer()) {
                stage = mSyntheticInputStage;
            } else {
                stage = q.shouldSkipIme() ? mFirstPostImeInputStage : mFirstInputStage;
            }
            if (q.mEvent instanceof KeyEvent) {
                try {
                    mUnhandledKeyManager.preDispatch((KeyEvent) q.mEvent);
                } finally {
                    Trace.traceEnd(Trace.TRACE_TAG_VIEW);
                }
            }
            if (stage != null) {
                handleWindowFocusChanged();
                stage.deliver(q);
            } else {
                finishInputEvent(q);
            }
        } finally {
        }
    }

在deliverInputEvent中出現(xiàn)了stage,這就是在setView初始化的那些處理器,處理通過stage.deliver(q)來實現(xiàn)。 InputStage 還是ViewRootImpl的一個內(nèi)部類。

 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;
        private String mTracePrefix;
        public InputStage(InputStage next) {
            mNext = next;
        }
        public final void deliver(QueuedInputEvent q) {
          //分發(fā)事件
            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;
        }
        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) {
          //向后一個 InputStage 傳遞事件
            if (mNext != null) {
                mNext.deliver(q);
            } else {
                finishInputEvent(q);
            }
        }
    }

熟悉okhttp的話很容易就發(fā)現(xiàn)這里也是一個責(zé)任鏈模式。從setView中 InputStage 子類的初始化也能看到,其中和view相關(guān)的是ViewPostImeInputStage。

 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();
              //判斷事件類型,觸摸事件會進入processPointerEvent
                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;
            mHandwritingInitiator.onTouchEvent(event);
            mAttachInfo.mUnbufferedDispatchRequested = false;
            mAttachInfo.mHandlingPointerEvent = true;
          //通過mView的dispatchPointerEvent來分發(fā)事件
            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;
        }

ViewRootImpl的事件就交給mView來繼續(xù)分發(fā)了,這里mView是DecorView,也是在setView中傳進來的。

DecorView事件處理

  //frameworks/base/core/java/android/view/View.java
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
    public final boolean dispatchPointerEvent(MotionEvent event) {
        if (event.isTouchEvent()) {
            return dispatchTouchEvent(event);
        } else {
            return dispatchGenericMotionEvent(event);
        }
    }
//frameworks/base/core/java/com/android/internal/policy/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);
    }

這里通過dispatchTouchEvent將事件交給了Window.Callback,而這里的Window.Callback就是Activity,兜兜轉(zhuǎn)轉(zhuǎn)終于回到了Activity的dispatchTouchEvent中。

通過這個流程可以知道,事件的流程是WMS->ViewRootImpl->DecorView->Activity->PhoneWindow->DecorView,這里有一個疑問就是為什么不直接從DecorView開始分發(fā)。我猜測是為了方便在應(yīng)用層重寫Activity中的onTouch來消費沒有view處理的事件。

到此,相信大家對“Andriod事件分發(fā)事件怎么來的”有了更深的了解,不妨來實際操作一番吧!這里是億速云網(wǎng)站,更多相關(guān)內(nèi)容可以進入相關(guān)頻道進行查詢,關(guān)注我們,繼續(xù)學(xué)習(xí)!

向AI問一下細節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI