Android觸摸事件的產(chǎn)生以及傳遞

前言

相信大家對(duì)Android的事件分發(fā)機(jī)制很熟悉,都知道事件分發(fā)是從ViewGroup的dispatchTouchEvent()開(kāi)始。那么你是否知道ViewGroup的事件是從哪里來(lái)的呢?本文將帶你去一探究竟。

事件的產(chǎn)生

總所周知,當(dāng)我們觸摸屏幕的時(shí)候就會(huì)有觸摸事件的產(chǎn)生,不過(guò)從在硬件的產(chǎn)生到我們應(yīng)用層的傳遞不在本文的討論范圍之內(nèi),有興趣的可以查看Android觸摸事件全過(guò)程分析。下面我們將從應(yīng)用層接受到事件開(kāi)始分析。

事件的傳遞

InputEventReceiver

    
    // Called from native code.
    @SuppressWarnings("unused")
    @UnsupportedAppUsage
    private void dispatchInputEvent(int seq, InputEvent event, int displayId) {
        mSeqMap.put(event.getSequenceNumber(), seq);
        onInputEvent(event, displayId);
    }

應(yīng)用層接受輸入事件的地方在InputEventReceiver的dispatchInputEvent(),從注釋可以看到這里完成了事件從native到應(yīng)用層的傳遞。在方法里面把事件通過(guò)onInputEvent()傳遞下去。接著就來(lái)到了InputEventReceiver的子類WindowInputEventReceiver,它重寫了onInputEvent()。

WindowInputEventReceiver

    @Override
    public void onInputEvent(InputEvent event, int displayId) {
        enqueueInputEvent(event, this, 0, true);
    }

WindowInputEventReceiver又是ViewRootImpl的內(nèi)部類,在里面它調(diào)用了ViewRootImpl的enqueueInputEvent()

ViewRootImpl

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

        // QueuedInputEvent是一個(gè)單鏈表結(jié)構(gòu),把輸入事件按順序連接起來(lái)
        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();
        }
    }
    
    //將InputEvent包裝進(jìn)QueuedInputEvent,和obtainMessage()很類似有木有
    private QueuedInputEvent obtainQueuedInputEvent(InputEvent event,
            InputEventReceiver receiver, int flags) {
        QueuedInputEvent q = mQueuedInputEventPool;
        if (q != null) {
            mQueuedInputEventPoolSize -= 1;
            mQueuedInputEventPool = q.mNext;
            q.mNext = null;
        } else {
            q = new QueuedInputEvent();
        }

        q.mEvent = event;
        q.mReceiver = receiver;
        q.mFlags = flags;   //注意這個(gè)flags,傳進(jìn)來(lái)的是0
        return q;
    }

從上面的調(diào)用我們知道processImmediately參數(shù)傳入的是true,所以接下來(lái)會(huì)調(diào)用doProcessInputEvents()

    void doProcessInputEvents() {
        // 使用while循環(huán),一個(gè)個(gè)的傳遞QueuedInputEvent中待處理的事件
        while (mPendingInputEventHead != null) {
            //取出第一個(gè)輸入事件,并從QueuedInputEvent鏈表中斷開(kāi)
            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);
        }
    }

從上面得知會(huì)通過(guò)deliverInputEvent()將事件一個(gè)個(gè)的傳遞下去

    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 (q.mEvent instanceof KeyEvent) {
            mUnhandledKeyManager.preDispatch((KeyEvent) q.mEvent);
        }

        if (stage != null) {
            handleWindowFocusChanged();
            stage.deliver(q);
        } else {
            finishInputEvent(q);
        }
    }
    
    //在前面的obtainQueuedInputEvent()我們知道,mFlags的值為0
    //所以這里的返回值為false
    public static final int FLAG_UNHANDLED = 1 << 5;
    public boolean shouldSendToSynthesizer() {
        if ((mFlags & FLAG_UNHANDLED) != 0) {
            return true;
        }

        return false;
    }
    
    
    public static final int FLAG_DELIVER_POST_IME = 1 << 0;
    public boolean shouldSkipIme() {
        if ((mFlags & FLAG_DELIVER_POST_IME) != 0) {
            return true;
        }
        //對(duì)于我們分析的MotionEvent來(lái)說(shuō)這是返回true的
        return mEvent instanceof MotionEvent
                && (mEvent.isFromSource(InputDevice.SOURCE_CLASS_POINTER)
                || mEvent.isFromSource(InputDevice.SOURCE_ROTARY_ENCODER);
    }

其中的InputStage是用于處理輸入事件的基類,里面有個(gè)mNext變量保存了下一個(gè)InputStage的引用,所以會(huì)把輸入事件一層層的傳遞下去進(jìn)行處理。

由上面的判斷可知,最終InputStage的值為mFirstPostImeInputStage,這個(gè)值會(huì)在setView()的時(shí)候被賦值。

  public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
        synchronized (this) {
            if (mView == null) {
                mView = view;
                ...
                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;   
            }
        }
  }

那么再回到上面,stage!=null,則它會(huì)調(diào)用deliver開(kāi)始傳遞輸入事件

   public static final int FLAG_FINISHED = 1 << 2;
    /**
     * Delivers an event to be processed.
     */
    public final void deliver(QueuedInputEvent q) {
        //mFlags==0
        if ((q.mFlags & QueuedInputEvent.FLAG_FINISHED) != 0) {
            forward(q);
        } else if (shouldDropInputEvent(q)) {
            finish(q, false);
        } else {
            apply(q, onProcess(q));
        }
    }
    
    //這里返回false
    protected boolean shouldDropInputEvent(QueuedInputEvent q) {
        if (mView == null || !mAdded) {
            Slog.w(mTag, "Dropping event due to root view being removed: " + q.mEvent);
            return true;
        } else if ((!mAttachInfo.mHasWindowFocus
            && !q.mEvent.isFromSource(InputDevice.SOURCE_CLASS_POINTER)
            && !isAutofillUiShowing()) || mStopped
            || (mIsAmbientMode && !q.mEvent.isFromSource(InputDevice.SOURCE_CLASS_BUTTON))
            || (mPausedForTransition && !isBack(q.mEvent))) {
            // This is a focus event and the window doesn't currently have input focus or
            // has stopped. This could be an event that came back from the previous stage
            // but the window has lost focus or stopped in the meantime.
            if (isTerminalInputEvent(q.mEvent)) {
                // Don't drop terminal input events, however mark them as canceled.
                q.mEvent.cancel();
                Slog.w(mTag, "Cancelling event due to no window focus: " + q.mEvent);
                return false;
            }

            // Drop non-terminal input events.
            Slog.w(mTag, "Dropping event due to no window focus: " + q.mEvent);
            return true;
        }
        return false;
    }

所以這里首先會(huì)進(jìn)入EarlyPostImeInputStage的apply(q, onProcess(q));,不過(guò)它并沒(méi)有處理掉輸入事件,而是進(jìn)一步的把事件傳遞下去,由于調(diào)用比較多這里就不展開(kāi)來(lái)說(shuō)了,有興趣的自行看源碼。我們直接來(lái)看下ViewPostImeInputStage的onProcess()方法。

ViewPostImeInputStage

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

因?yàn)槲覀兪怯|摸事件,所以會(huì)進(jìn)入到processPointerEvent()

    private int processPointerEvent(QueuedInputEvent q) {
        final MotionEvent event = (MotionEvent)q.mEvent;

        mAttachInfo.mUnbufferedDispatchRequested = false;
        mAttachInfo.mHandlingPointerEvent = true;
        
        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;
    }

可以看到其中調(diào)用了mView.dispatchPointerEvent(event),這個(gè)mView就是上面在setView()的時(shí)候賦值的,值為DecorView。而DecorView繼承于FrameLayout,所以我們看下View的dispatchPointerEvent()的具體實(shí)現(xiàn)即可。

View

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

因?yàn)槭怯|摸事件,所以會(huì)執(zhí)行dispatchTouchEvent(),而DecorView重寫了該方法。

DecordView

    @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就是PhoneWindow,那么它的Callback是誰(shuí)呢?沒(méi)錯(cuò),就是我們最熟悉的Activity了。

Activity

    public class Activity extends ContextThemeWrapper
        implements LayoutInflater.Factory2,
        Window.Callback, KeyEvent.Callback,
        OnCreateContextMenuListener, ComponentCallbacks2,
        Window.OnWindowDismissedCallback, WindowControllerCallback,
        AutofillManager.AutofillClient
        
    final void attach(Context context, ActivityThread aThread,
            Instrumentation instr, IBinder token, int ident,
            Application application, Intent intent, ActivityInfo info,
            CharSequence title, Activity parent, String id,
            NonConfigurationInstances lastNonConfigurationInstances,
            Configuration config, String referrer, IVoiceInteractor voiceInteractor,
            Window window, ActivityConfigCallback activityConfigCallback) {
        attachBaseContext(context);

        mFragments.attachHost(null /*parent*/);

        mWindow = new PhoneWindow(this, window, activityConfigCallback);
        mWindow.setWindowControllerCallback(this);
        mWindow.setCallback(this);
        mWindow.setOnWindowDismissedCallback(this);
    }
    
    public boolean dispatchTouchEvent(MotionEvent ev) {
        if (ev.getAction() == MotionEvent.ACTION_DOWN) {
            onUserInteraction();
        }
        if (getWindow().superDispatchTouchEvent(ev)) {
            return true;
        }
        return onTouchEvent(ev);
    }

Activity實(shí)現(xiàn)了Window的Callback接口,在attach()中實(shí)例化了PhoneWindow,并設(shè)置了Callback。但是在Activity的dispatchTouchEvent()中,又把觸摸事件傳遞給了PhoneWindow的superDispatchTouchEvent()。

PhoneWindow

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

嗯?又調(diào)用mDecor.superDispatchTouchEvent(),這個(gè)mDecor仍然是DecorView。

DecordView

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

最終就回到了我們的dispatchTouchEvent(),從而開(kāi)始了我們熟悉的事件分發(fā)機(jī)制。

總結(jié)

觸摸事件由硬件處產(chǎn)生,在應(yīng)用層的傳遞流程為InputEventReceiver->ViewRootImpl->DecorView->Activity(實(shí)現(xiàn)了Window.Callback)->PhoneWindow->DecorView->ViewGroup->View

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容