Android重學(xué)系列 IMS與事件分發(fā)(下)

前言

上一篇文章和大家聊到了IMS在SystemServer進(jìn)程native層中的原理,本文來(lái)聊聊App進(jìn)程是怎么監(jiān)聽(tīng)I(yíng)MS分發(fā)出來(lái)的輸入信號(hào)的.

正文

還記得我寫(xiě)過(guò)WMS系列文章WMS在Activity啟動(dòng)中的職責(zé) 添加窗體(三)中盏缤,提到了App第一次渲染的時(shí)候會(huì)通過(guò)ViewRootImpl的addWindow方法憔古,在WMS中為當(dāng)前的Activity中的PhoneWindow添加一個(gè)對(duì)應(yīng)的WindowState進(jìn)行管理皂甘。

讓我們先看看ViewRootImpl中做了什么恋追。

如果遇到什么問(wèn)題佳魔,歡迎來(lái)到本文http://www.reibang.com/p/e26bb5fae995下討論

ViewRootImpl setView

文件:/frameworks/base/core/java/android/view/ViewRootImpl.java

    public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
        synchronized (this) {
            if (mView == null) {
                mView = view;

....

                requestLayout();
                if ((mWindowAttributes.inputFeatures
                        & WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
//核心事件一
                    mInputChannel = new InputChannel();
                }
                mForceDecorViewVisibility = (mWindowAttributes.privateFlags
                        & PRIVATE_FLAG_FORCE_DECOR_VIEW_VISIBILITY) != 0;
                try {
//核心事件二
                    res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
                            getHostVisibility(), mDisplay.getDisplayId(), mWinFrame,
                            mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
                            mAttachInfo.mOutsets, mAttachInfo.mDisplayCutout, mInputChannel);
                } catch (RemoteException e) {
...
                } finally {
...
                }

...
//核心事件三
                if (mInputChannel != null) {
                    if (mInputQueueCallback != null) {
                        mInputQueue = new InputQueue();
                        mInputQueueCallback.onInputQueueCreated(mInputQueue);
                    }
                    mInputEventReceiver = new WindowInputEventReceiver(mInputChannel,
                            Looper.myLooper());
                }

...
                // 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;
            }
        }
    }

在這個(gè)過(guò)程中曙聂,我們可以把它視作三大部分的邏輯

  • 1.沒(méi)有為當(dāng)前的ViewRootImpl初始化InputChannel,則會(huì)先創(chuàng)建一個(gè)InputChannel鞠鲜。

  • 2.接著把InputChannel對(duì)象通過(guò)Session的addToDisplay宁脊,也就是addWindow發(fā)送到WMS中進(jìn)行處理。詳細(xì)的邏輯請(qǐng)看WMS在Activity啟動(dòng)中的職責(zé) 添加窗體(三)贤姆。

  • 3.最后為ViewRootImpl構(gòu)建接受從InputChannel發(fā)送回來(lái)的輸入事件環(huán)境榆苞。

核心就是第二和第三點(diǎn)。先來(lái)看看第二點(diǎn)庐氮,Session的addToDisplay最后是調(diào)用到了WMS的addWindow中语稠。

WMS addWindow

文件:/frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java

    public int addWindow(Session session, IWindow client, int seq,
            LayoutParams attrs, int viewVisibility, int displayId, Rect outFrame,
            Rect outContentInsets, Rect outStableInsets, Rect outOutsets,
            DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel) {
...
        synchronized(mWindowMap) {

...

            final WindowState win = new WindowState(this, session, client, token, parentWindow,
                    appOp[0], seq, attrs, viewVisibility, session.mUid,
                    session.mCanAddInternalSystemWindow);

...

            final boolean openInputChannels = (outInputChannel != null
                    && (attrs.inputFeatures & INPUT_FEATURE_NO_INPUT_CHANNEL) == 0);
            if  (openInputChannels) {
                win.openInputChannel(outInputChannel);
            }

...
            mInputMonitor.setUpdateInputWindowsNeededLw();

            boolean focusChanged = false;
            if (win.canReceiveKeys()) {
                focusChanged = updateFocusedWindowLocked(UPDATE_FOCUS_WILL_ASSIGN_LAYERS,
                        false /*updateInputWindows*/);
                if (focusChanged) {
                    imMayMove = false;
                }
            }


            if (focusChanged) {
                mInputMonitor.setInputFocusLw(mCurrentFocus, false /*updateInputWindows*/);
            }
            mInputMonitor.updateInputWindowsLw(false /*force*/);


        }
...

        return res;
    }

我們把InputChannel相關(guān)的邏輯抽離出來(lái):

  • 1.首先如果當(dāng)前的Window對(duì)應(yīng)IWindow沒(méi)有對(duì)應(yīng)在WMS的mWindowMap,則會(huì)創(chuàng)建一個(gè)全新的WindowState對(duì)應(yīng)上弄砍。并且調(diào)用WindowState的openInputChannel初始化從ViewRootImpl傳過(guò)來(lái)的InputChannel

  • 2.使用InputMonitor更新當(dāng)前的焦點(diǎn)窗口仙畦。

我們來(lái)看看WindowState的openInputChannel方法。

WindowState

文件:/frameworks/base/services/core/java/com/android/server/wm/WindowState.java

    WindowState(WindowManagerService service, Session s, IWindow c, WindowToken token,
            WindowState parentWindow, int appOp, int seq, WindowManager.LayoutParams a,
            int viewVisibility, int ownerId, boolean ownerCanAddInternalSystemWindow,
            PowerManagerWrapper powerManagerWrapper) {
        super(service);
....
        mInputWindowHandle = new InputWindowHandle(
                mAppToken != null ? mAppToken.mInputApplicationHandle : null, this, c,
                    getDisplayId());
    }

能看到實(shí)際上這個(gè)過(guò)程誕生了一個(gè)很重要的對(duì)象InputWindowHandle音婶,輸入窗口的句柄慨畸。這個(gè)句柄最核心的對(duì)象就是通過(guò)WindowToken獲取AppToken的InputApplicationHandle。

WindowState openInputChannel


    void openInputChannel(InputChannel outInputChannel) {

        String name = getName();
        InputChannel[] inputChannels = InputChannel.openInputChannelPair(name);
        mInputChannel = inputChannels[0];
        mClientChannel = inputChannels[1];
        mInputWindowHandle.inputChannel = inputChannels[0];
        if (outInputChannel != null) {
            mClientChannel.transferTo(outInputChannel);
            mClientChannel.dispose();
            mClientChannel = null;
        } else {
            mDeadWindowEventReceiver = new DeadWindowEventReceiver(mClientChannel);
        }
        mService.mInputManager.registerInputChannel(mInputChannel, mInputWindowHandle);
    }

能看到這個(gè)過(guò)程衣式,實(shí)際上和上一篇文章十分相似的monitorInput一節(jié)中的內(nèi)容十分相似寸士。

依次執(zhí)行了如下的邏輯:

  • 1.openInputChannelPair 為Java層的InputChannel在native創(chuàng)建一對(duì)InputChannel。
  • 2.mInputWindowHandle 持有InputChannel對(duì)的0號(hào)對(duì)應(yīng)的InputChannel
  • 3.把1號(hào)位置中的NativeInputChannel賦值給ViewRootImpl傳遞過(guò)來(lái)的InputChannel碴卧。并關(guān)閉InputChannel對(duì)的1號(hào)位置對(duì)應(yīng)的InputChannel弱卡。
  • 4.把0號(hào)位置的InputChannel注冊(cè)到IMS底層中,監(jiān)聽(tīng)輸入時(shí)間的到來(lái)住册。

這樣通過(guò)socketpair創(chuàng)建的一對(duì)socket對(duì)象婶博,注冊(cè)了一個(gè)新的發(fā)送端到IMS的native層中,就能被App端的InputChannel監(jiān)聽(tīng)到荧飞。

從這里就可以知道凡人,0號(hào)位置的InputChannel對(duì)應(yīng)的socket就是服務(wù)端(發(fā)送端)名党。關(guān)于如何創(chuàng)建InputChannel,以及如何注冊(cè)到IMS挠轴。這里就不多贅述传睹,請(qǐng)閱讀IMS與事件分發(fā)(上)

ViewRootImpl 構(gòu)建輸入事件的監(jiān)聽(tīng)環(huán)境

                if (mInputChannel != null) {
...
                    mInputEventReceiver = new WindowInputEventReceiver(mInputChannel,
                            Looper.myLooper());
                }

...
                // 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;
  • 1.在ViewRootImpl中構(gòu)建一個(gè)WindowInputEventReceiver對(duì)象岸晦,這個(gè)對(duì)象將會(huì)監(jiān)聽(tīng)從IMS傳送過(guò)來(lái)的輸入事件欧啤。
  • 2.構(gòu)建InputStage對(duì)象,該系列對(duì)象實(shí)際上就是當(dāng)IMS從native傳遞上來(lái)后委煤,進(jìn)行處理的輸入事件"舞臺(tái)".

WindowInputEventReceiver ViewRootImpl對(duì)輸入事件的監(jiān)聽(tīng)原理

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

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

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

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

這個(gè)對(duì)象很簡(jiǎn)單堂油,他繼承于InputEventReceiver。InputEventReceiver對(duì)象就是專(zhuān)門(mén)監(jiān)聽(tīng)I(yíng)MS輸入事件的基類(lèi)。每當(dāng)IMS發(fā)送信號(hào)來(lái)了就會(huì)調(diào)用子類(lèi)的onInputEvent方法,onBatchedInputEventPending守屉。

我們先來(lái)看看InputEventReceiver的初始化拦英。

InputEventReceiver

    public InputEventReceiver(InputChannel inputChannel, Looper looper) {
...
        mInputChannel = inputChannel;
        mMessageQueue = looper.getQueue();
        mReceiverPtr = nativeInit(new WeakReference<InputEventReceiver>(this),
                inputChannel, mMessageQueue);

        mCloseGuard.open("dispose");
    }

核心實(shí)際上就是調(diào)用native方法在native層初始化了IMS事件監(jiān)聽(tīng)器。

InputEventReceiver native層初始化

文件:/frameworks/base/core/jni/android_view_InputEventReceiver.cpp

static jlong nativeInit(JNIEnv* env, jclass clazz, jobject receiverWeak,
        jobject inputChannelObj, jobject messageQueueObj) {
    sp<InputChannel> inputChannel = android_view_InputChannel_getInputChannel(env,
            inputChannelObj);
...
    sp<MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj);
...
    sp<NativeInputEventReceiver> receiver = new NativeInputEventReceiver(env,
            receiverWeak, inputChannel, messageQueue);
    status_t status = receiver->initialize();
...
    receiver->incStrong(gInputEventReceiverClassInfo.clazz); // retain a reference for the object
    return reinterpret_cast<jlong>(receiver.get());
}

這里只是簡(jiǎn)單的生成一個(gè)NativeInputEventReceiver對(duì)象院峡,并調(diào)用了NativeInputEventReceiver的initialize方法兴使。為全局的clazz對(duì)象新增一個(gè)強(qiáng)引用計(jì)數(shù)。

NativeInputEventReceiver

class NativeInputEventReceiver : public LooperCallback {
public:
    NativeInputEventReceiver(JNIEnv* env,
            jobject receiverWeak, const sp<InputChannel>& inputChannel,
            const sp<MessageQueue>& messageQueue);

    status_t initialize();
    void dispose();
    status_t finishInputEvent(uint32_t seq, bool handled);
    status_t consumeEvents(JNIEnv* env, bool consumeBatches, nsecs_t frameTime,
            bool* outConsumedBatch);

protected:
    virtual ~NativeInputEventReceiver();

private:
    struct Finish {
        uint32_t seq;
        bool handled;
    };

    jobject mReceiverWeakGlobal;
    InputConsumer mInputConsumer;
    sp<MessageQueue> mMessageQueue;
    PreallocatedInputEventFactory mInputEventFactory;
    bool mBatchedInputEventPending;
    int mFdEvents;
    Vector<Finish> mFinishQueue;
    void setFdEvents(int events);

    const std::string getInputChannelName() {
        return mInputConsumer.getChannel()->getName();
    }

    virtual int handleEvent(int receiveFd, int events, void* data);
};

從NativeInputEventReceiver的申明能看到實(shí)際上他是實(shí)現(xiàn)了LooperCallback照激。LooperCallback這個(gè)對(duì)象发魄,可以閱讀Handler與相關(guān)系統(tǒng)調(diào)用的剖析(上),里面有講解到LooperCallback實(shí)際上就是native層Looper回調(diào)后的監(jiān)聽(tīng)對(duì)象俩垃,回調(diào)的方法就是虛函數(shù)handleEvent励幼。

在NativeInputEventReceiver有一個(gè)十分重要的對(duì)象InputConsumer。當(dāng)IMS回調(diào)了輸入事件后口柳,NativeInputEventReceiver使用InputConsumer在native層中進(jìn)行處理苹粟。

構(gòu)造函數(shù)沒(méi)什么好看的,直接看看initialize初始化的方法跃闹。

NativeInputEventReceiver initialize

status_t NativeInputEventReceiver::initialize() {
    setFdEvents(ALOOPER_EVENT_INPUT);
    return OK;
}

void NativeInputEventReceiver::setFdEvents(int events) {
    if (mFdEvents != events) {
        mFdEvents = events;
        int fd = mInputConsumer.getChannel()->getFd();
        if (events) {
            mMessageQueue->getLooper()->addFd(fd, 0, events, this, NULL);
        } else {
            mMessageQueue->getLooper()->removeFd(fd);
        }
    }
}

能看到這里面實(shí)際上很簡(jiǎn)單嵌削,就是獲取InputConsumer中的InputChannel中的fd,這里fd就是上面初始化好的接收端的InputChannel望艺。因此就是獲取主線程的Looper并使用Looper監(jiān)聽(tīng)客戶(hù)端的InputChannel苛秕。

一旦IMS有信號(hào)發(fā)送過(guò)來(lái)則立即回調(diào)LooperCallback中的handleEvent。

當(dāng)輸入信號(hào)從native層傳送過(guò)來(lái)了找默,則會(huì)開(kāi)始回調(diào)handleEvent方法艇劫。關(guān)于IMS如果讀取輸入事件,處理后傳輸過(guò)來(lái)啡莉,可以閱讀我寫(xiě)的IMS與事件分發(fā)(上)港准。

handleEvent App進(jìn)程處理輸入事件

int NativeInputEventReceiver::handleEvent(int receiveFd, int events, void* data) {
    if (events & (ALOOPER_EVENT_ERROR | ALOOPER_EVENT_HANGUP)) {
        return 0; // remove the callback
    }

    if (events & ALOOPER_EVENT_INPUT) {
        JNIEnv* env = AndroidRuntime::getJNIEnv();
        status_t status = consumeEvents(env, false /*consumeBatches*/, -1, NULL);
        mMessageQueue->raiseAndClearException(env, "handleReceiveCallback");
        return status == OK || status == NO_MEMORY ? 1 : 0;
    }

    if (events & ALOOPER_EVENT_OUTPUT) {
        for (size_t i = 0; i < mFinishQueue.size(); i++) {
            const Finish& finish = mFinishQueue.itemAt(i);
            status_t status = mInputConsumer.sendFinishedSignal(finish.seq, finish.handled);
            if (status) {
                mFinishQueue.removeItemsAt(0, i);

                if (status == WOULD_BLOCK) {
                    return 1; // keep the callback, try again later
                }

...
                return 0; // remove the callback
            }
        }

        mFinishQueue.clear();
        setFdEvents(ALOOPER_EVENT_INPUT);
        return 1;
    }

    return 1;
}

大致上可以分為兩種情況旨剥,分別對(duì)象Looper注冊(cè)的事件類(lèi)型ALOOPER_EVENT_INPUT和ALOOPER_EVENT_OUTPUT。

很多地方?jīng)]解析清楚:

  • ALOOPER_EVENT_INPUT 是指那些可讀的文件描述符傳遞過(guò)來(lái)的事件
  • ALOOPER_EVENT_OUTPUT 是指那些可寫(xiě)的文件描述符浅缸,需要傳遞過(guò)去的事件轨帜。

在NativeInputEventReceiver中,ALOOPER_EVENT_INPUT代表從驅(qū)動(dòng)讀取到的輸入事件傳遞過(guò)來(lái)衩椒;ALOOPER_EVENT_OUTPUT代表此時(shí)需要關(guān)閉輸入事件的監(jiān)聽(tīng)蚌父,而傳遞過(guò)去的后返回的事件處理。

我們先來(lái)看看ALOOPER_EVENT_INPUT對(duì)應(yīng)的事件處理毛萌。

    if (events & ALOOPER_EVENT_INPUT) {
        JNIEnv* env = AndroidRuntime::getJNIEnv();
        status_t status = consumeEvents(env, false /*consumeBatches*/, -1, NULL);
        mMessageQueue->raiseAndClearException(env, "handleReceiveCallback");
        return status == OK || status == NO_MEMORY ? 1 : 0;
    }

核心處理方法是consumeEvents苟弛。

consumeEvents


status_t NativeInputEventReceiver::consumeEvents(JNIEnv* env,
        bool consumeBatches, nsecs_t frameTime, bool* outConsumedBatch) {

    if (consumeBatches) {
        mBatchedInputEventPending = false;
    }
    if (outConsumedBatch) {
        *outConsumedBatch = false;
    }

    ScopedLocalRef<jobject> receiverObj(env, NULL);
    bool skipCallbacks = false;
    for (;;) {
        uint32_t seq;
        InputEvent* inputEvent;
        int32_t displayId;
        status_t status = mInputConsumer.consume(&mInputEventFactory,
                consumeBatches, frameTime, &seq, &inputEvent, &displayId);
        if (status) {
            if (status == WOULD_BLOCK) {
                if (!skipCallbacks && !mBatchedInputEventPending
                        && mInputConsumer.hasPendingBatch()) {

                    mBatchedInputEventPending = true;

                    env->CallVoidMethod(receiverObj.get(),
                            gInputEventReceiverClassInfo.dispatchBatchedInputEventPending);

                }
                return OK;
            }

            return status;
        }
...
        if (!skipCallbacks) {
....
            jobject inputEventObj;
            switch (inputEvent->getType()) {
            case AINPUT_EVENT_TYPE_KEY:

                inputEventObj = android_view_KeyEvent_fromNative(env,
                        static_cast<KeyEvent*>(inputEvent));
                break;

            case AINPUT_EVENT_TYPE_MOTION: {

                MotionEvent* motionEvent = static_cast<MotionEvent*>(inputEvent);
                if ((motionEvent->getAction() & AMOTION_EVENT_ACTION_MOVE) && outConsumedBatch) {
                    *outConsumedBatch = true;
                }
                inputEventObj = android_view_MotionEvent_obtainAsCopy(env, motionEvent);
                break;
            }

            default:
                inputEventObj = NULL;
            }

            if (inputEventObj) {
//發(fā)送核心
                env->CallVoidMethod(receiverObj.get(),
                        gInputEventReceiverClassInfo.dispatchInputEvent, seq, inputEventObj,
                        displayId);
                if (env->ExceptionCheck()) {
                    
                    skipCallbacks = true;
                }
                env->DeleteLocalRef(inputEventObj);
            } else {
                skipCallbacks = true;
            }
        }

        if (skipCallbacks) {
            mInputConsumer.sendFinishedSignal(seq, false);
        }
    }
}
  • 1.通過(guò)InputConsumer的consume方法消費(fèi)它持有的InputChannel的輸入事件。

  • 3.如果是Monition類(lèi)型事件且是多點(diǎn)觸控需要批量處理的阁将,則會(huì)通過(guò)CallVoidMethod反射調(diào)用InputEventReceiver的dispatchBatchedInputEventPending方法膏秫。

  • 2.根據(jù)Key 還是Monition生成對(duì)應(yīng)的Java對(duì)象,通過(guò)CallVoidMethod反射調(diào)用Java方法做盅,InputEventReceiver的dispatchInputEvent方法缤削。

InputConsumer consume

文件:/frameworks/native/libs/input/InputTransport.cpp

status_t InputConsumer::consume(InputEventFactoryInterface* factory,
        bool consumeBatches, nsecs_t frameTime, uint32_t* outSeq, InputEvent** outEvent,
        int32_t* displayId) {

    *outSeq = 0;
    *outEvent = NULL;
    *displayId = -1;  // Invalid display.


    while (!*outEvent) {
        if (mMsgDeferred) {

            mMsgDeferred = false;
        } else {

            status_t result = mChannel->receiveMessage(&mMsg);
            if (result) {

                if (consumeBatches || result != WOULD_BLOCK) {
                    result = consumeBatch(factory, frameTime, outSeq, outEvent, displayId);
                    if (*outEvent) {

                        break;
                    }
                }
                return result;
            }
        }

        switch (mMsg.header.type) {
        case InputMessage::TYPE_KEY: {
            KeyEvent* keyEvent = factory->createKeyEvent();
            if (!keyEvent) return NO_MEMORY;

            initializeKeyEvent(keyEvent, &mMsg);
            *outSeq = mMsg.body.key.seq;
            *outEvent = keyEvent;

            break;
        }

        case InputMessage::TYPE_MOTION: {
            ssize_t batchIndex = findBatch(mMsg.body.motion.deviceId, mMsg.body.motion.source);
            if (batchIndex >= 0) {
                Batch& batch = mBatches.editItemAt(batchIndex);
                if (canAddSample(batch, &mMsg)) {
                    batch.samples.push(mMsg);

                    break;
                } else {

                    mMsgDeferred = true;
                    status_t result = consumeSamples(factory,
                            batch, batch.samples.size(), outSeq, outEvent, displayId);
                    mBatches.removeAt(batchIndex);
                    if (result) {
                        return result;
                    }

                    break;
                }
            }

            // Start a new batch if needed.
            if (mMsg.body.motion.action == AMOTION_EVENT_ACTION_MOVE
                    || mMsg.body.motion.action == AMOTION_EVENT_ACTION_HOVER_MOVE) {
                mBatches.push();
                Batch& batch = mBatches.editTop();
                batch.samples.push(mMsg);

                break;
            }

            MotionEvent* motionEvent = factory->createMotionEvent();
            if (! motionEvent) return NO_MEMORY;

            updateTouchState(mMsg);
            initializeMotionEvent(motionEvent, &mMsg);
            *outSeq = mMsg.body.motion.seq;
            *outEvent = motionEvent;
            *displayId = mMsg.body.motion.displayId;
            break;
        }

        default:
            return UNKNOWN_ERROR;
        }
    }
    return OK;
}

先從InputChannel的recv系統(tǒng)調(diào)用獲取socket里面的InputMessage數(shù)據(jù)。

雖然此時(shí)consumeBatches為false吹榴,但是result正常情況下不會(huì)是WOULD_BLOCK,會(huì)先執(zhí)行consumeBatch批量處理觸點(diǎn)事件亭敢。

在這個(gè)方法中分為兩個(gè)類(lèi)型處理:

  • 1.InputMessage::TYPE_KEY 是key按鍵類(lèi)型,則通過(guò)上面?zhèn)飨聛?lái)的factory構(gòu)建一個(gè)KeyEvent對(duì)象图筹,初始化后并且返回帅刀。

  • 2.InputMessage::TYPE_MOTION 是觸點(diǎn)類(lèi)型。由于觸點(diǎn)類(lèi)型可以是多點(diǎn)觸碰远剩,對(duì)于移動(dòng)的觸點(diǎn)扣溺,需要進(jìn)行觸點(diǎn)的跟蹤,因此這里引入了Batch概念民宿,按照批次處理觸點(diǎn)事件娇妓。

    struct Batch {
        Vector<InputMessage> samples;
    };

能看到實(shí)際上Batch就是一個(gè)InputMessage的集合。每當(dāng)檢測(cè)到AMOTION_EVENT_ACTION_MOVE或者AMOTION_EVENT_ACTION_HOVER_MOVE的觸點(diǎn)類(lèi)型活鹰,則會(huì)添加到mBatches集合中哈恰,等待下一次的更新。

當(dāng)下一次觸點(diǎn)觸發(fā)了回調(diào)志群,在這個(gè)outEvent鏈表不為空的循環(huán)前提下着绷,canAddSample判斷到當(dāng)前PointerCount和之前的一致,會(huì)把InputMessage不斷的添加到Batch的samples集合中锌云。如果出現(xiàn)了不一致則需要consumeSamples進(jìn)行更新Batch中記錄的InputMessage荠医。

這樣就能跟蹤到了這一批次的觸點(diǎn)的軌跡,以及新增的觸點(diǎn)。

如果只有單個(gè)觸點(diǎn)則生成MotionEvent對(duì)象賦值給指針?lè)祷亍?/p>

我們來(lái)看看InputEventReceiver是通過(guò)InputConsumer消費(fèi)后是怎么觸發(fā)接下來(lái)的邏輯彬向。我們只看單點(diǎn)觸發(fā)的邏輯兼贡。

InputReceiver 分發(fā)輸入事件

                env->CallVoidMethod(receiverObj.get(),
                        gInputEventReceiverClassInfo.dispatchInputEvent, seq, inputEventObj,
                        displayId);

實(shí)際上對(duì)應(yīng)的是:

    private void dispatchInputEvent(int seq, InputEvent event, int displayId) {
        mSeqMap.put(event.getSequenceNumber(), seq);
        onInputEvent(event, displayId);
    }

而onInputEvent這個(gè)方法實(shí)際上就是對(duì)應(yīng)WindowInputEventReceiver。

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

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

可以看到最后回調(diào)到了enqueueInputEvent方法中娃胆。

enqueueInputEvent
    void enqueueInputEvent(InputEvent event) {
        enqueueInputEvent(event, null, 0, false);
    }

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

        QueuedInputEvent last = mPendingInputEventTail;
        if (last == null) {
            mPendingInputEventHead = q;
            mPendingInputEventTail = q;
        } else {
            last.mNext = q;
            mPendingInputEventTail = q;
        }
        mPendingInputEventCount += 1;

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

能看到整個(gè)很久愛(ài)都難遍希,就是生成一個(gè)obtainQueuedInputEvent對(duì)象,添加到mPendingInputEventTail鏈表的末端里烦,調(diào)用scheduleProcessInputEvents方法分發(fā)凿蒜。如果是需要立即響應(yīng)則調(diào)用doProcessInputEvents方法。

scheduleProcessInputEvents
    private void scheduleProcessInputEvents() {
        if (!mProcessInputEventsScheduled) {
            mProcessInputEventsScheduled = true;
            Message msg = mHandler.obtainMessage(MSG_PROCESS_INPUT_EVENTS);
            msg.setAsynchronous(true);
            mHandler.sendMessage(msg);
        }
    }

能看到此時(shí)發(fā)送了一個(gè)MSG_PROCESS_INPUT_EVENTS一個(gè)Asynchronous異步消息胁黑。其實(shí)就是一個(gè)能在同步屏障內(nèi)優(yōu)先執(zhí)行的消息废封。

                case MSG_PROCESS_INPUT_EVENTS:
                    mProcessInputEventsScheduled = false;
                    doProcessInputEvents();
                    break;

核心還是調(diào)用了doProcessInputEvents。

doProcessInputEvents
    void doProcessInputEvents() {
        while (mPendingInputEventHead != null) {
            QueuedInputEvent q = mPendingInputEventHead;
            mPendingInputEventHead = q.mNext;
            if (mPendingInputEventHead == null) {
                mPendingInputEventTail = null;
            }
            q.mNext = null;

            mPendingInputEventCount -= 1;

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

        if (mProcessInputEventsScheduled) {
            mProcessInputEventsScheduled = false;
            mHandler.removeMessages(MSG_PROCESS_INPUT_EVENTS);
        }
    }

Choreographer.mFrameInfo 更新了分發(fā)時(shí)間后丧蘸,整個(gè)過(guò)程最核心的邏輯就是循環(huán)遍歷mPendingInputEventHead調(diào)用deliverInputEvent進(jìn)行事件的分發(fā)QueuedInputEvent漂洋。

deliverInputEvent
    private void deliverInputEvent(QueuedInputEvent q) {
        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);
        }
    }

邏輯分為如下幾個(gè)步驟:

  • 1.QueuedInputEvent的shouldSendToSynthesizer判斷默認(rèn)是false,shouldSkipIme也是false触趴。此時(shí)InputStage就是mFirstInputStage氮发。這個(gè)對(duì)象就是NativePreImeInputStage。

  • 2.如果獲取到的stage不為空冗懦,則調(diào)用NativePreImeInputStage的deliver方法分發(fā)事件。

ViewRootImpl 構(gòu)建輸入事件的接收環(huán)境

                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;

能看到這里面構(gòu)建很多InputStage對(duì)象仇祭。這些對(duì)象都是通過(guò)責(zé)任鏈設(shè)計(jì)全部嵌套到一起披蕉。

我們簡(jiǎn)單的看看它的UML圖,來(lái)區(qū)分他們的直接的關(guān)系:


InputStage.png

InputStage 的分發(fā)入口

先來(lái)看看InputStage的deliver

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

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

        protected void onDeliverToNext(QueuedInputEvent q) {
            if (mNext != null) {
                mNext.deliver(q);
            } else {
                finishInputEvent(q);
            }
        }


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

deliver的入口會(huì)判斷當(dāng)前QueuedInputEvent的狀態(tài)乌奇。

  • 如果判斷QueuedInputEvent打開(kāi)FLAG_FINISHED標(biāo)志位没讲,換句話說(shuō)就是不是通過(guò)finish方法進(jìn)來(lái)的,就會(huì)執(zhí)行forward的方法礁苗。

  • 如果判斷到當(dāng)前Window失去焦點(diǎn)爬凑,或者還沒(méi)有進(jìn)行刷新ui,QueuedInputEvent則執(zhí)行finish

  • 剩下的情況執(zhí)行apply的默認(rèn)方法试伙,而執(zhí)行的方法由每一個(gè)InputStage的子類(lèi)復(fù)寫(xiě)onProcess標(biāo)志位決定的嘁信。

我們來(lái)看看對(duì)整個(gè)鏈路從NativePreImeInputStage開(kāi)始逆推回去,關(guān)鍵還是看apply中的方法疏叨。

在所有的InputStage中分為兩類(lèi)潘靖,一類(lèi)是直接繼承InputStage,一類(lèi)是繼承AsyncInputStage蚤蔓,我們優(yōu)先看看AsyncInputStage卦溢。

AsyncInputStage

abstract class AsyncInputStage extends InputStage {
        private final String mTraceCounter;

        private QueuedInputEvent mQueueHead;
        private QueuedInputEvent mQueueTail;
        private int mQueueLength;

        protected static final int DEFER = 3;

    ....

        protected void defer(QueuedInputEvent q) {
            q.mFlags |= QueuedInputEvent.FLAG_DEFERRED;
            enqueue(q);
        }

        @Override
        protected void forward(QueuedInputEvent q) {
            q.mFlags &= ~QueuedInputEvent.FLAG_DEFERRED;

            QueuedInputEvent curr = mQueueHead;
            if (curr == null) {
                super.forward(q);
                return;
            }

            final int deviceId = q.mEvent.getDeviceId();
            QueuedInputEvent prev = null;
            boolean blocked = false;
            while (curr != null && curr != q) {
                if (!blocked && deviceId == curr.mEvent.getDeviceId()) {
                    blocked = true;
                }
                prev = curr;
                curr = curr.mNext;
            }

            if (blocked) {
                if (curr == null) {
                    enqueue(q);
                }
                return;
            }

            if (curr != null) {
                curr = curr.mNext;
                dequeue(q, prev);
            }
            super.forward(q);

            while (curr != null) {
                if (deviceId == curr.mEvent.getDeviceId()) {
                    if ((curr.mFlags & QueuedInputEvent.FLAG_DEFERRED) != 0) {
                        break;
                    }
                    QueuedInputEvent next = curr.mNext;
                    dequeue(curr, prev);
                    super.forward(curr);
                    curr = next;
                } else {
                    prev = curr;
                    curr = curr.mNext;
                }
            }
        }

        private void enqueue(QueuedInputEvent q) {
            if (mQueueTail == null) {
                mQueueHead = q;
                mQueueTail = q;
            } else {
                mQueueTail.mNext = q;
                mQueueTail = q;
            }

            mQueueLength += 1;
        }

        private void dequeue(QueuedInputEvent q, QueuedInputEvent prev) {
            if (prev == null) {
                mQueueHead = q.mNext;
            } else {
                prev.mNext = q.mNext;
            }
            if (mQueueTail == q) {
                mQueueTail = prev;
            }
            q.mNext = null;

            mQueueLength -= 1;

        }
}

在AsyncInputStage存儲(chǔ)了一個(gè)QueuedInputEvent鏈表。當(dāng)判斷到事件打開(kāi)了FLAG_FINISHED,其在核心方法forward做了如下的事情:

  • 當(dāng)鏈表中沒(méi)有任何待分發(fā)的事件单寂,直接調(diào)用父類(lèi)的forward方法贬芥,也就調(diào)用onDeliverNext方法,在onDeliverNext如果當(dāng)前InputStage不存在下一個(gè)InputStage則會(huì)調(diào)用finishInputEvent宣决。

  • 當(dāng)存在待分發(fā)的事件鏈表蘸劈,則會(huì)嘗試判斷是否已經(jīng)存在相同的輸入設(shè)備(也就是相同的輸入類(lèi)型)相同事件對(duì)象。

    • 如果找到了相同的輸入設(shè)備id則block為true疲扎,找到相同事件對(duì)象或者末尾則跳出循環(huán)昵时。
      • 如果遍歷剛好在末尾,說(shuō)明沒(méi)有相同的事件則通過(guò)enqueue添加到事件鏈表末尾椒丧。
      • 如果curr不為空壹甥,說(shuō)明此時(shí)有相同的事件則dequeue 出隊(duì)當(dāng)前的輸入事件,調(diào)用父類(lèi)forward壶熏。
      • 如果經(jīng)過(guò)forward的處理句柠,事件隊(duì)列還存在輸入事件關(guān)閉FLAG_DEFERRED標(biāo)志位的QueuedInputEvent,則繼續(xù)遍歷鏈表進(jìn)行消費(fèi)棒假。
finishInputEvent
    private void finishInputEvent(QueuedInputEvent q) {

        if (q.mReceiver != null) {
            boolean handled = (q.mFlags & QueuedInputEvent.FLAG_FINISHED_HANDLED) != 0;
            q.mReceiver.finishInputEvent(q.mEvent, handled);
        } else {
            ...
        }

        recycleQueuedInputEvent(q);
    }

能看到這個(gè)過(guò)程中很簡(jiǎn)單溯职,如果QueuedInputEvent持有了InputEventReceiver對(duì)象則會(huì)InputEventReceiver.finishInputEvent進(jìn)行native方法的調(diào)用,告訴native層銷(xiāo)毀了當(dāng)前的事件帽哑。

    public final void finishInputEvent(InputEvent event, boolean handled) {
        if (event == null) {
            throw new IllegalArgumentException("event must not be null");
        }
        if (mReceiverPtr == 0) {
....
        } else {
            int index = mSeqMap.indexOfKey(event.getSequenceNumber());
            if (index < 0) {
...
            } else {
                int seq = mSeqMap.valueAt(index);
                mSeqMap.removeAt(index);
                nativeFinishInputEvent(mReceiverPtr, seq, handled);
            }
        }
        event.recycleIfNeededAfterDispatch();
    }
NativeInputEventReceiver finishInputEvent
status_t NativeInputEventReceiver::finishInputEvent(uint32_t seq, bool handled) {

    status_t status = mInputConsumer.sendFinishedSignal(seq, handled);
    if (status) {
        if (status == WOULD_BLOCK) {
            Finish finish;
            finish.seq = seq;
            finish.handled = handled;
            mFinishQueue.add(finish);
            if (mFinishQueue.size() == 1) {
                setFdEvents(ALOOPER_EVENT_INPUT | ALOOPER_EVENT_OUTPUT);
            }
            return OK;
        }
    }
    return status;
}

能看到很簡(jiǎn)單就是調(diào)用InputConsumer的sendFinishedSignal方法發(fā)送該輸入事件的序列號(hào)處理對(duì)應(yīng)在InputDispatcher中事件谜酒。

InputStage分類(lèi)

當(dāng)InputStage需要開(kāi)始分發(fā)事件,就會(huì)調(diào)用apply方法妻枕,而apply中就會(huì)調(diào)用onProcess方法僻族。每一個(gè)子類(lèi)InputStage的onProcess其實(shí)就是意味著這個(gè)InputStage做了什么事情。

接下來(lái)我們就按照責(zé)任鏈的嵌套順序來(lái)看看InputStage屡谐,每一個(gè)輸入階段都做了什么述么。

NativePreImeInputStage
    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;
        }
...
    }

NativePreImeInputStage實(shí)際上就是就是處理InputQueue。

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 這個(gè)InputStage是預(yù)處理KeyEvent愕掏,把鍵盤(pán)等事件通過(guò)DecorView的dispatchKeyEventPreIme進(jìn)行預(yù)處理分發(fā)度秘。

ImeInputStage
    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;
                    int result = imm.dispatchInputEvent(event, q, this, mHandler);
                    if (result == InputMethodManager.DISPATCH_HANDLED) {
                        return FINISH_HANDLED;
                    } else if (result == InputMethodManager.DISPATCH_NOT_HANDLED) {
                        return FORWARD;
                    } else {
                        return DEFER; // callback will be invoked later
                    }
                }
            }
            return FORWARD;
        }

...
    }

ImeInputStage專(zhuān)門(mén)處理軟鍵盤(pán)的事件分發(fā)。

EarlyPostImeInputStage
 final class EarlyPostImeInputStage extends InputStage {
        public EarlyPostImeInputStage(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);
                }
            }
            return FORWARD;
        }

        private int processKeyEvent(QueuedInputEvent q) {
            final KeyEvent event = (KeyEvent)q.mEvent;

            if (mAttachInfo.mTooltipHost != null) {
                mAttachInfo.mTooltipHost.handleTooltipKey(event);
            }

            if (checkForLeavingTouchModeAndConsume(event)) {
                return FINISH_HANDLED;
            }

            mFallbackEventHandler.preDispatchKeyEvent(event);
            return FORWARD;
        }

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

            if (mTranslator != null) {
                mTranslator.translateEventInScreenToAppWindow(event);
            }

            final int action = event.getAction();
            if (action == MotionEvent.ACTION_DOWN || action == MotionEvent.ACTION_SCROLL) {
                ensureTouchMode(event.isFromSource(InputDevice.SOURCE_TOUCHSCREEN));
            }

            if (action == MotionEvent.ACTION_DOWN) {
                // Upon motion event within app window, close autofill ui.
                AutofillManager afm = getAutofillManager();
                if (afm != null) {
                    afm.requestHideFillUi();
                }
            }

            if (action == MotionEvent.ACTION_DOWN && mAttachInfo.mTooltipHost != null) {
                mAttachInfo.mTooltipHost.hideTooltip();
            }

            if (mCurScrollY != 0) {
                event.offsetLocation(0, mCurScrollY);
            }

            if (event.isTouchEvent()) {
                mLastTouchPoint.x = event.getRawX();
                mLastTouchPoint.y = event.getRawY();
                mLastTouchSource = event.getSource();
            }
            return FORWARD;
        }
    }

該方法實(shí)際上是處理mFallbackEventHandler的Key事件饵撑。這個(gè)對(duì)象是PhoneFallbackEventHandler剑梳,里面處理了手機(jī)屏幕外按鍵的事件處理,如多媒體音量肄梨,通話音量等等阻荒。還處理了Touch模式以及AutofillManager。

NativePostImeInputStage
    final class NativePostImeInputStage extends AsyncInputStage
            implements InputQueue.FinishedInputEventCallback {
        public NativePostImeInputStage(InputStage next, String traceCounter) {
            super(next, traceCounter);
        }

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

...
    }

NativePostImeInputStage繼續(xù)處理了之前還需要繼續(xù)處理InputQueue中的事件众羡。

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();
                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);
                }
            }
        }
....
}
  • 判斷是KeyEvent類(lèi)型則processKeyEvent開(kāi)始分發(fā)KeyEntry
  • 如果不是KeyEvent侨赡,但是是手指輸入設(shè)備,則調(diào)用processPointerEvent。最終會(huì)調(diào)用View 的dispatchPointerEvent
    • 如果來(lái)自SOURCE_CLASS_TRACKBALL輸入設(shè)備羊壹,則調(diào)用processTrackballEvent蓖宦。最終會(huì)調(diào)用View 的dispatchTrackballEvent
    • 剩下的則會(huì)通過(guò)processGenericMotionEvent分發(fā)Monition。會(huì)調(diào)用View的dispatchGenericMotionEvent方法油猫。
SyntheticInputStage
    final class SyntheticInputStage extends InputStage {
        private final SyntheticTrackballHandler mTrackball = new SyntheticTrackballHandler();
        private final SyntheticJoystickHandler mJoystick = new SyntheticJoystickHandler();
        private final SyntheticTouchNavigationHandler mTouchNavigation =
                new SyntheticTouchNavigationHandler();
        private final SyntheticKeyboardHandler mKeyboard = new SyntheticKeyboardHandler();

        public SyntheticInputStage() {
            super(null);
        }

        @Override
        protected int onProcess(QueuedInputEvent q) {
            q.mFlags |= QueuedInputEvent.FLAG_RESYNTHESIZED;
            if (q.mEvent instanceof MotionEvent) {
                final MotionEvent event = (MotionEvent)q.mEvent;
                final int source = event.getSource();
                if ((source & InputDevice.SOURCE_CLASS_TRACKBALL) != 0) {
                    mTrackball.process(event);
                    return FINISH_HANDLED;
                } else if ((source & InputDevice.SOURCE_CLASS_JOYSTICK) != 0) {
                    mJoystick.process(event);
                    return FINISH_HANDLED;
                } else if ((source & InputDevice.SOURCE_TOUCH_NAVIGATION)
                        == InputDevice.SOURCE_TOUCH_NAVIGATION) {
                    mTouchNavigation.process(event);
                    return FINISH_HANDLED;
                }
            } else if ((q.mFlags & QueuedInputEvent.FLAG_UNHANDLED) != 0) {
                mKeyboard.process((KeyEvent)q.mEvent);
                return FINISH_HANDLED;
            }

            return FORWARD;
        }
        @Override
        protected void onDeliverToNext(QueuedInputEvent q) {
            if ((q.mFlags & QueuedInputEvent.FLAG_RESYNTHESIZED) == 0) {
                if (q.mEvent instanceof MotionEvent) {
                    final MotionEvent event = (MotionEvent)q.mEvent;
                    final int source = event.getSource();
                    if ((source & InputDevice.SOURCE_CLASS_TRACKBALL) != 0) {
                        mTrackball.cancel();
                    } else if ((source & InputDevice.SOURCE_CLASS_JOYSTICK) != 0) {
                        mJoystick.cancel();
                    } else if ((source & InputDevice.SOURCE_TOUCH_NAVIGATION)
                            == InputDevice.SOURCE_TOUCH_NAVIGATION) {
                        mTouchNavigation.cancel(event);
                    }
                }
            }
            super.onDeliverToNext(q);
        }
...
}

在對(duì)剩下不同的設(shè)備輸入事件進(jìn)行通過(guò)對(duì)應(yīng)的處理對(duì)象進(jìn)行enqueue處理稠茂。

View觸點(diǎn)事件的分發(fā)

在這么多的InputStage 輸入處理階段對(duì)象中,需要我們進(jìn)行重點(diǎn)關(guān)注的是ViewPostImeInputStage情妖。在這個(gè)階段中對(duì)Key和Motion對(duì)象進(jìn)行處理睬关。

Key事件分發(fā)

        private int processKeyEvent(QueuedInputEvent q) {
            final KeyEvent event = (KeyEvent)q.mEvent;

....
            // Deliver the key to the view hierarchy.
            if (mView.dispatchKeyEvent(event)) {
                return FINISH_HANDLED;
            }
...
            return FORWARD;
        }

實(shí)際上此時(shí)的mView是DecorView。通過(guò)根布局的dispatchKeyEvent向整個(gè)View視圖層級(jí)分發(fā)毡证。

DecorView dispatchKeyEvent

    @Override
    public boolean dispatchKeyEvent(KeyEvent event) {
        final int keyCode = event.getKeyCode();
        final int action = event.getAction();
        final boolean isDown = action == KeyEvent.ACTION_DOWN;

...
        if (!mWindow.isDestroyed()) {
            final Window.Callback cb = mWindow.getCallback();
            final boolean handled = cb != null && mFeatureId < 0 ? cb.dispatchKeyEvent(event)
                    : super.dispatchKeyEvent(event);
            if (handled) {
                return true;
            }
        }

        return isDown ? mWindow.onKeyDown(mFeatureId, event.getKeyCode(), event)
                : mWindow.onKeyUp(mFeatureId, event.getKeyCode(), event);
    }

DecorView會(huì)校驗(yàn)它持有的PhoneWindow是否被銷(xiāo)毀电爹。沒(méi)有銷(xiāo)毀則獲取PhoneWindow的Window.Callback監(jiān)聽(tīng)對(duì)象,調(diào)用它的dispatchKeyEvent方法料睛。

如果判斷dispatchKeyEvent處理的事件返回false丐箩,說(shuō)明需要繼續(xù)處理Key事件。因此此時(shí)發(fā)現(xiàn)當(dāng)前的KeyEvent是ACTION_DOWN恤煞,則會(huì)調(diào)用PhoneWindow的onKeyDown方法屎勘,否則則調(diào)用onKeyUp。

我們主要來(lái)考察Key的事件分發(fā).注意此時(shí)正在監(jiān)聽(tīng)Window.Callback的回調(diào)是Activity居扒。

Activity dispatchKeyEvent

    public boolean dispatchKeyEvent(KeyEvent event) {
        onUserInteraction();

        final int keyCode = event.getKeyCode();
        if (keyCode == KeyEvent.KEYCODE_MENU &&
                mActionBar != null && mActionBar.onMenuKeyEvent(event)) {
            return true;
        }

        Window win = getWindow();
        if (win.superDispatchKeyEvent(event)) {
            return true;
        }
        View decor = mDecor;
        if (decor == null) decor = win.getDecorView();
        return event.dispatch(this, decor != null
                ? decor.getKeyDispatcherState() : null, this);
    }

Activity獲取PhoneWindow對(duì)象概漱,調(diào)用PhoneWindow的superDispatchKeyEvent。

PhoneWindow superDispatchKeyEvent
    public boolean superDispatchKeyEvent(KeyEvent event) {
        return mDecor.superDispatchKeyEvent(event);
    }
DecorView superDispatchKeyEvent
    public boolean superDispatchKeyEvent(KeyEvent event) {
...

        if (super.dispatchKeyEvent(event)) {
            return true;
        }

        return (getViewRootImpl() != null) && getViewRootImpl().dispatchUnhandledKeyEvent(event);
    }

調(diào)用了核心的了ViewGroup的dispatchKeyEvent方法喜喂。

ViewGroup dispatchKeyEvent
    @Override
    public boolean dispatchKeyEvent(KeyEvent event) {
        if (mInputEventConsistencyVerifier != null) {
            mInputEventConsistencyVerifier.onKeyEvent(event, 1);
        }

        if ((mPrivateFlags & (PFLAG_FOCUSED | PFLAG_HAS_BOUNDS))
                == (PFLAG_FOCUSED | PFLAG_HAS_BOUNDS)) {
            if (super.dispatchKeyEvent(event)) {
                return true;
            }
        } else if (mFocused != null && (mFocused.mPrivateFlags & PFLAG_HAS_BOUNDS)
                == PFLAG_HAS_BOUNDS) {
            if (mFocused.dispatchKeyEvent(event)) {
                return true;
            }
        }

        if (mInputEventConsistencyVerifier != null) {
            mInputEventConsistencyVerifier.onUnhandledEvent(event, 1);
        }
        return false;
    }

在ViewGroup中會(huì)記錄當(dāng)前的焦點(diǎn)View犀概。如果是當(dāng)前的ViewGroup帶上了焦點(diǎn),則會(huì)調(diào)用父類(lèi)的dispatchKeyEvent方法夜惭。否則則嘗試的查找當(dāng)前的ViewGroup中焦點(diǎn)View的dispatchKeyEvent繼續(xù)分發(fā)Key事件。

View dispatchKeyEvent
    public boolean dispatchKeyEvent(KeyEvent event) {
        if (mInputEventConsistencyVerifier != null) {
            mInputEventConsistencyVerifier.onKeyEvent(event, 0);
        }


        ListenerInfo li = mListenerInfo;
        if (li != null && li.mOnKeyListener != null && (mViewFlags & ENABLED_MASK) == ENABLED
                && li.mOnKeyListener.onKey(this, event.getKeyCode(), event)) {
            return true;
        }

        if (event.dispatch(this, mAttachInfo != null
                ? mAttachInfo.mKeyDispatchState : null, this)) {
            return true;
        }

        if (mInputEventConsistencyVerifier != null) {
            mInputEventConsistencyVerifier.onUnhandledEvent(event, 0);
        }
        return false;
    }

能夠在這里看到了此時(shí)會(huì)回調(diào)我們給當(dāng)前View設(shè)置的mOnKeyListener回調(diào)onKey方法铛绰。

這樣就完成了對(duì)Key事件的監(jiān)聽(tīng)诈茧。

ViewRootImpl Motion觸點(diǎn)事件分發(fā)

我們回到ViewPostImeInputStage中對(duì)Motion的觸點(diǎn)事件處理processPointerEvent的考察。


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

            mAttachInfo.mUnbufferedDispatchRequested = false;
            mAttachInfo.mHandlingPointerEvent = true;
            boolean handled = mView.dispatchPointerEvent(event);
...
            return handled ? FINISH_HANDLED : FORWARD;
        }

很簡(jiǎn)單就是調(diào)用了DecorView的dispatchPointerEvent方法捂掰。而DecorView的dispatchPointerEvent就是調(diào)用了View的dispatchPointerEvent

View dispatchPointerEvent

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

這里就會(huì)判斷是否是觸點(diǎn)事件敢会,如果是則調(diào)用dispatchTouchEvent方法,否則則dispatchGenericMotionEvent處理这嚣。我們考察dispatchTouchEvent觸點(diǎn)事件的分發(fā)鸥昏。

ViewGroup dispatchTouchEvent

從這個(gè)方法開(kāi)始,就是我們熟悉的事件分發(fā)處理:

    public boolean dispatchTouchEvent(MotionEvent ev) {
...
        boolean handled = false;
        if (onFilterTouchEventForSecurity(ev)) {
            final int action = ev.getAction();
            final int actionMasked = action & MotionEvent.ACTION_MASK;

            if (actionMasked == MotionEvent.ACTION_DOWN) {
                cancelAndClearTouchTargets(ev);
                resetTouchState();
            }

            final boolean intercepted;
            if (actionMasked == MotionEvent.ACTION_DOWN
                    || mFirstTouchTarget != null) {
                final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
                if (!disallowIntercept) {
//核心事件1
                    intercepted = onInterceptTouchEvent(ev);
                    ev.setAction(action); // restore action in case it was changed
                } else {
                    intercepted = false;
                }
            } else {
                intercepted = true;
            }


...
            final boolean canceled = resetCancelNextUpFlag(this)
                    || actionMasked == MotionEvent.ACTION_CANCEL;

            final boolean split = (mGroupFlags & FLAG_SPLIT_MOTION_EVENTS) != 0;
            TouchTarget newTouchTarget = null;
            boolean alreadyDispatchedToNewTouchTarget = false;
            if (!canceled && !intercepted) {

                View childWithAccessibilityFocus = ev.isTargetAccessibilityFocus()
                        ? findChildWithAccessibilityFocus() : null;

                if (actionMasked == MotionEvent.ACTION_DOWN
                        || (split && actionMasked == MotionEvent.ACTION_POINTER_DOWN)
                        || actionMasked == MotionEvent.ACTION_HOVER_MOVE) {
                    final int actionIndex = ev.getActionIndex(); // always 0 for down
                    final int idBitsToAssign = split ? 1 << ev.getPointerId(actionIndex)
                            : TouchTarget.ALL_POINTER_IDS;

                    removePointersFromTouchTargets(idBitsToAssign);

                    final int childrenCount = mChildrenCount;
                    if (newTouchTarget == null && childrenCount != 0) {
                        final float x = ev.getX(actionIndex);
                        final float y = ev.getY(actionIndex);
                        final ArrayList<View> preorderedList = buildTouchDispatchChildList();
                        final boolean customOrder = preorderedList == null
                                && isChildrenDrawingOrderEnabled();
                        final View[] children = mChildren;
                        for (int i = childrenCount - 1; i >= 0; i--) {
                            final int childIndex = getAndVerifyPreorderedIndex(
                                    childrenCount, i, customOrder);
                            final View child = getAndVerifyPreorderedView(
                                    preorderedList, children, childIndex);

                            if (!canViewReceivePointerEvents(child)
                                    || !isTransformedTouchPointInView(x, y, child, null)) {
                                ev.setTargetAccessibilityFocus(false);
                                continue;
                            }

                            newTouchTarget = getTouchTarget(child);
                            if (newTouchTarget != null) {
                                newTouchTarget.pointerIdBits |= idBitsToAssign;
                                break;
                            }

                            if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
                                mLastTouchDownTime = ev.getDownTime();
                                if (preorderedList != null) {
                                    // childIndex points into presorted list, find original index
                                    for (int j = 0; j < childrenCount; j++) {
                                        if (children[childIndex] == mChildren[j]) {
                                            mLastTouchDownIndex = j;
                                            break;
                                        }
                                    }
                                } else {
                                    mLastTouchDownIndex = childIndex;
                                }
                                mLastTouchDownX = ev.getX();
                                mLastTouchDownY = ev.getY();
                                newTouchTarget = addTouchTarget(child, idBitsToAssign);
                                alreadyDispatchedToNewTouchTarget = true;
                                break;
                            }

...
                        }
                        if (preorderedList != null) preorderedList.clear();
                    }

                    if (newTouchTarget == null && mFirstTouchTarget != null) {
                        newTouchTarget = mFirstTouchTarget;
                        while (newTouchTarget.next != null) {
                            newTouchTarget = newTouchTarget.next;
                        }
                        newTouchTarget.pointerIdBits |= idBitsToAssign;
                    }
                }
            }

            // Dispatch to touch targets.
            if (mFirstTouchTarget == null) {
                handled = dispatchTransformedTouchEvent(ev, canceled, null,
                        TouchTarget.ALL_POINTER_IDS);
            } else {

                TouchTarget predecessor = null;
                TouchTarget target = mFirstTouchTarget;
                while (target != null) {
                    final TouchTarget next = target.next;
                    if (alreadyDispatchedToNewTouchTarget && target == newTouchTarget) {
                        handled = true;
                    } else {
                        final boolean cancelChild = resetCancelNextUpFlag(target.child)
                                || intercepted;
                        if (dispatchTransformedTouchEvent(ev, cancelChild,
                                target.child, target.pointerIdBits)) {
                            handled = true;
                        }
                        if (cancelChild) {
                            if (predecessor == null) {
                                mFirstTouchTarget = next;
                            } else {
                                predecessor.next = next;
                            }
                            target.recycle();
                            target = next;
                            continue;
                        }
                    }
                    predecessor = target;
                    target = next;
                }
            }

            if (canceled
                    || actionMasked == MotionEvent.ACTION_UP
                    || actionMasked == MotionEvent.ACTION_HOVER_MOVE) {
                resetTouchState();
            } else if (split && actionMasked == MotionEvent.ACTION_POINTER_UP) {
                final int actionIndex = ev.getActionIndex();
                final int idBitsToRemove = 1 << ev.getPointerId(actionIndex);
                removePointersFromTouchTargets(idBitsToRemove);
            }
        }

...
        return handled;
    }

在這個(gè)過(guò)程中如下執(zhí)行了幾個(gè)核心邏輯:

  • 1.onInterceptTouchEvent 校驗(yàn)當(dāng)前的ViewGroup是否需要攔截當(dāng)前事件分發(fā)到子View姐帚。
  • 2.如果不進(jìn)行攔截事件分發(fā)吏垮,則代表可以繼續(xù)分發(fā)觸點(diǎn)事件。首先會(huì)對(duì)當(dāng)前ViewGroup中所有的子View先按照z軸的順序排序。然后按照這個(gè)順序遍歷每一個(gè)子View.
    • 1.為了進(jìn)行優(yōu)化膳汪,ViewGroup會(huì)記錄TouchTarget對(duì)象鏈表唯蝶。TouchTarget這個(gè)鏈表實(shí)際上就是記錄每一次可以進(jìn)行焦點(diǎn)處理的子View。通過(guò)isTransformedTouchPointInView方法校驗(yàn)當(dāng)前的觸點(diǎn)是否在子View范圍中遗嗽,如果當(dāng)前能夠獲取到TouchTarget對(duì)象粘我,則跳出當(dāng)前遍歷z軸順序的循環(huán)。并在下面一個(gè)新循環(huán)中處理dispatchTransformedTouchEvent痹换。
    • 2.如果TouchTarget中獲取不到有效的觸點(diǎn)對(duì)象征字,說(shuō)明該View已經(jīng)清空了一次TouchTarget鏈表或者第一次。則會(huì)dispatchTransformedTouchEvent處理每一個(gè)子View成功后娇豫,為對(duì)應(yīng)的子View添加一個(gè)對(duì)應(yīng)的TouchTarget匙姜。

來(lái)看看isTransformedTouchPointInView是怎么判斷觸點(diǎn)事件在View的范圍:

    protected boolean isTransformedTouchPointInView(float x, float y, View child,
            PointF outLocalPoint) {
        final float[] point = getTempPoint();
        point[0] = x;
        point[1] = y;
        transformPointToViewLocal(point, child);
        final boolean isInView = child.pointInView(point[0], point[1]);
        if (isInView && outLocalPoint != null) {
            outLocalPoint.set(point[0], point[1]);
        }
        return isInView;
    }

    public boolean pointInView(float localX, float localY, float slop) {
        return localX >= -slop && localY >= -slop && localX < ((mRight - mLeft) + slop) &&
                localY < ((mBottom - mTop) + slop);
    }

很簡(jiǎn)單知道子View的四個(gè)邊緣和滑動(dòng)的距離,只要在這四個(gè)區(qū)域內(nèi)即可锤躁。

核心分發(fā)給子View的核心是dispatchTransformedTouchEvent搁料。

ViewGroup dispatchTransformedTouchEvent
    private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,
            View child, int desiredPointerIdBits) {
        final boolean handled;

        final int oldAction = event.getAction();
...
        final int oldPointerIdBits = event.getPointerIdBits();
        final int newPointerIdBits = oldPointerIdBits & desiredPointerIdBits;

        if (newPointerIdBits == 0) {
            return false;
        }

        final MotionEvent transformedEvent;
...

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

        // Done.
        transformedEvent.recycle();
        return handled;
    }
  • 如果child為null,說(shuō)明可能在這個(gè)ViewGroup中沒(méi)找到需要觸點(diǎn)處理的子View系羞。則調(diào)用了父類(lèi)View的dispatchTouchEvent郭计。

  • 如果child不為null,則調(diào)用該子View的dispatchTouchEvent方法椒振。

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

        boolean result = false;

        if (mInputEventConsistencyVerifier != null) {
            mInputEventConsistencyVerifier.onTouchEvent(event, 0);
        }

        final int actionMasked = event.getActionMasked();
        if (actionMasked == MotionEvent.ACTION_DOWN) {
            stopNestedScroll();
        }

        if (onFilterTouchEventForSecurity(event)) {
            if ((mViewFlags & ENABLED_MASK) == ENABLED && handleScrollBarDragging(event)) {
                result = true;
            }

            ListenerInfo li = mListenerInfo;
            if (li != null && li.mOnTouchListener != null
                    && (mViewFlags & ENABLED_MASK) == ENABLED
                    && li.mOnTouchListener.onTouch(this, event)) {
                result = true;
            }

            if (!result && onTouchEvent(event)) {
                result = true;
            }
        }

        if (!result && mInputEventConsistencyVerifier != null) {
            mInputEventConsistencyVerifier.onUnhandledEvent(event, 0);
        }

        if (actionMasked == MotionEvent.ACTION_UP ||
                actionMasked == MotionEvent.ACTION_CANCEL ||
                (actionMasked == MotionEvent.ACTION_DOWN && !result)) {
            stopNestedScroll();
        }

        return result;
    }

在這個(gè)過(guò)程中按照順序執(zhí)行如下的步驟:

  • 1.判斷當(dāng)前是ACTION_DOWN 則暫驼焉欤滑動(dòng)
  • 2.判斷mOnTouchListener不為空,先執(zhí)行mOnTouchListener的onTouch方法澎迎。
  • 3.回調(diào)onTouchEvent方法
  • 4.判斷到是ACTION_UP或者ACTION_CANCEL或者ACTION_DOWN庐杨,且不是拽動(dòng)則暫停滑動(dòng)夹供。
View onTouchEvent
    public boolean onTouchEvent(MotionEvent event) {
        final float x = event.getX();
        final float y = event.getY();
        final int viewFlags = mViewFlags;
        final int action = event.getAction();

        final boolean clickable = ((viewFlags & CLICKABLE) == CLICKABLE
                || (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)
                || (viewFlags & CONTEXT_CLICKABLE) == CONTEXT_CLICKABLE;

        if ((viewFlags & ENABLED_MASK) == DISABLED) {
            if (action == MotionEvent.ACTION_UP && (mPrivateFlags & PFLAG_PRESSED) != 0) {
                setPressed(false);
            }
            mPrivateFlags3 &= ~PFLAG3_FINGER_DOWN;
            return clickable;
        }
        if (mTouchDelegate != null) {
            if (mTouchDelegate.onTouchEvent(event)) {
                return true;
            }
        }

        if (clickable || (viewFlags & TOOLTIP) == TOOLTIP) {
            switch (action) {
                case MotionEvent.ACTION_UP:
                    mPrivateFlags3 &= ~PFLAG3_FINGER_DOWN;
...
                    boolean prepressed = (mPrivateFlags & PFLAG_PREPRESSED) != 0;
                    if ((mPrivateFlags & PFLAG_PRESSED) != 0 || prepressed) {
                        boolean focusTaken = false;
                        if (isFocusable() && isFocusableInTouchMode() && !isFocused()) {
                            focusTaken = requestFocus();
                        }

                        if (prepressed) {
                            setPressed(true, x, y);
                        }

                        if (!mHasPerformedLongPress && !mIgnoreNextUpEvent) {
                            removeLongPressCallback();
                            if (!focusTaken) {
                                if (mPerformClick == null) {
                                    mPerformClick = new PerformClick();
                                }
                                if (!post(mPerformClick)) {
                                    performClickInternal();
                                }
                            }
                        }

                        if (mUnsetPressedState == null) {
                            mUnsetPressedState = new UnsetPressedState();
                        }

                        if (prepressed) {
                            postDelayed(mUnsetPressedState,
                                    ViewConfiguration.getPressedStateDuration());
                        } else if (!post(mUnsetPressedState)) {
                            mUnsetPressedState.run();
                        }

                        removeTapCallback();
                    }
                    mIgnoreNextUpEvent = false;
                    break;

                case MotionEvent.ACTION_DOWN:
...
                    break;

                case MotionEvent.ACTION_CANCEL:
...
                    break;

                case MotionEvent.ACTION_MOVE:
....
                    break;
            }

            return true;
        }

        return false;
    }

我們只需要關(guān)注Up手勢(shì)中做了比較重要的邏輯:

  • 如果可以進(jìn)行聚焦灵份,但是沒(méi)有焦點(diǎn)則先requestFocus進(jìn)行焦點(diǎn)的請(qǐng)求
  • 如果prepressed為true,則調(diào)用setPressed把下按狀態(tài)設(shè)置為true
  • 調(diào)用post發(fā)送PerformClick的runnable哮洽,如果發(fā)送失敗則調(diào)用performClickInternal直接發(fā)送onClick方法填渠。
    public boolean performClick() {
        notifyAutofillManagerOnClick();

        final boolean result;
        final ListenerInfo li = mListenerInfo;
        if (li != null && li.mOnClickListener != null) {
            playSoundEffect(SoundEffectConstants.CLICK);
            li.mOnClickListener.onClick(this);
            result = true;
        } else {
            result = false;
        }

        sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);

        notifyEnterOrExitForAutoFillIfNeeded(true);

        return result;
    }

總結(jié)

到這里就結(jié)束了對(duì)IMS相關(guān)的邏輯分析。

根據(jù)上一次的設(shè)計(jì)圖鸟辅,來(lái)展示更加完整的結(jié)構(gòu)圖


IMS_App設(shè)計(jì).png

App進(jìn)程初始化IMS的監(jiān)聽(tīng):

  • 當(dāng)Activity初始化后氛什,在resume生命周期,會(huì)調(diào)用ViewRootImpl的setView方法匪凉。
  • 在這個(gè)方法中枪眉,會(huì)調(diào)用addWindow,把初始化好的InputChannel傳送到WMS的WindowState中再层。WindowState會(huì)為InputChannel初始化一對(duì)socket文件描述符贸铜,一端在監(jiān)聽(tīng)I(yíng)MS的事件發(fā)送堡纬,另一段是監(jiān)聽(tīng)發(fā)送的到來(lái)。
  • App主線程的WindowInputEventReceiver 對(duì)象會(huì)通過(guò)Looper會(huì)監(jiān)聽(tīng)I(yíng)nputChannel的接收端萨脑。一旦接收端有事件發(fā)送到來(lái)隐轩,就會(huì)喚醒Looper在InputConsumer中進(jìn)行消費(fèi)。
    • InputConsumer消費(fèi)觸點(diǎn)對(duì)象后渤早,會(huì)回調(diào)到WindowInputEventReceiver中职车,調(diào)用Looper發(fā)送一個(gè)IMS發(fā)送對(duì)象,準(zhǔn)備在InputStage中進(jìn)行處理鹊杖。

InputStage是輸入事件的處理階段悴灵,是一種很典型的責(zé)任鏈設(shè)計(jì)模式,每一個(gè)處理階段都會(huì)知道下一個(gè)處理階段是什么骂蓖,這種設(shè)計(jì)在App開(kāi)發(fā)中十分常見(jiàn)积瞒,對(duì)于冗長(zhǎng)的業(yè)務(wù),我們可以通過(guò)這種設(shè)計(jì)靈活的進(jìn)行解藕登下。

  • NativePreImeInputStage 預(yù)處理InputQueue
  • ViewPreImeInputStage 這個(gè)InputStage是預(yù)處理KeyEvent茫孔,把鍵盤(pán)等事件通過(guò)DecorView的dispatchKeyEventPreIme進(jìn)行預(yù)處理分發(fā)。
  • ImeInputStage專(zhuān)門(mén)處理軟鍵盤(pán)的事件分發(fā)
  • EarlyPostImeInputStage 處理mFallbackEventHandler的Key事件被芳。這個(gè)對(duì)象是PhoneFallbackEventHandler缰贝,里面處理了手機(jī)屏幕外按鍵的事件處理,如多媒體音量畔濒,通話音量等等剩晴。還處理了Touch模式以及AutofillManager
  • NativePostImeInputStage繼續(xù)處理了之前還需要繼續(xù)處理InputQueue中的事件
  • ViewPostImeInputStage 對(duì)Key和Motion進(jìn)行View層級(jí)的事件分發(fā)
  • SyntheticInputStage 根據(jù)設(shè)備進(jìn)行不同的輸入事件入隊(duì)處理(如觸屏球等)。

關(guān)于InputQueue和SyntheticInputStage我們不需要過(guò)多的關(guān)注侵状。我們App開(kāi)發(fā)還是主要關(guān)注ViewPostImeInputStage是如何分發(fā)的赞弥。

事件分發(fā)的流程順序:

  • ViewGroup的dispatchTouchEvent 分發(fā)事件
  • ViewGroup的onInterceptTouchEvent 攔截事件
  • View的dispatchTouchEvent
  • onTouchListener.onTouch 的監(jiān)聽(tīng)回調(diào)
  • onTouchEvent 方法回調(diào)
  • onClickListener.onClick 當(dāng)是手勢(shì)抬起時(shí),點(diǎn)擊事件回調(diào)

這個(gè)流程是面試最常見(jiàn)的問(wèn)題之一趣兄,記住即可绽左。

后記

原計(jì)劃是準(zhǔn)備聊聊PMS的安裝Apk原理。不過(guò)艇潭,我個(gè)人覺(jué)得還是先把另外三大組件都聊一邊妇菱,我們?cè)倩貋?lái)聊聊PMS的安裝原理。

然后以PMS安裝的dex文件的介紹暴区,來(lái)開(kāi)始Android art虛擬機(jī)的原理介紹。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末辛臊,一起剝皮案震驚了整個(gè)濱河市仙粱,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌彻舰,老刑警劉巖伐割,帶你破解...
    沈念sama閱讀 206,311評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件候味,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡隔心,警方通過(guò)查閱死者的電腦和手機(jī)白群,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)硬霍,“玉大人帜慢,你說(shuō)我怎么就攤上這事∥簦” “怎么了粱玲?”我有些...
    開(kāi)封第一講書(shū)人閱讀 152,671評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)拜轨。 經(jīng)常有香客問(wèn)我抽减,道長(zhǎng),這世上最難降的妖魔是什么橄碾? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,252評(píng)論 1 279
  • 正文 為了忘掉前任卵沉,我火速辦了婚禮,結(jié)果婚禮上法牲,老公的妹妹穿的比我還像新娘史汗。我一直安慰自己,他們只是感情好皆串,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,253評(píng)論 5 371
  • 文/花漫 我一把揭開(kāi)白布淹办。 她就那樣靜靜地躺著,像睡著了一般恶复。 火紅的嫁衣襯著肌膚如雪怜森。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 49,031評(píng)論 1 285
  • 那天谤牡,我揣著相機(jī)與錄音副硅,去河邊找鬼。 笑死翅萤,一個(gè)胖子當(dāng)著我的面吹牛恐疲,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播套么,決...
    沈念sama閱讀 38,340評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼培己,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了胚泌?” 一聲冷哼從身側(cè)響起省咨,我...
    開(kāi)封第一講書(shū)人閱讀 36,973評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎玷室,沒(méi)想到半個(gè)月后零蓉,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體笤受,經(jīng)...
    沈念sama閱讀 43,466評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,937評(píng)論 2 323
  • 正文 我和宋清朗相戀三年敌蜂,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了箩兽。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,039評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡章喉,死狀恐怖汗贫,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情囊陡,我是刑警寧澤芳绩,帶...
    沈念sama閱讀 33,701評(píng)論 4 323
  • 正文 年R本政府宣布,位于F島的核電站撞反,受9級(jí)特大地震影響妥色,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜遏片,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,254評(píng)論 3 307
  • 文/蒙蒙 一嘹害、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧吮便,春花似錦笔呀、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,259評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至僚匆,卻和暖如春微渠,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背咧擂。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,485評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工逞盆, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人松申。 一個(gè)月前我還...
    沈念sama閱讀 45,497評(píng)論 2 354
  • 正文 我出身青樓云芦,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親贸桶。 傳聞我的和親對(duì)象是個(gè)殘疾皇子舅逸,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,786評(píng)論 2 345