Android Input子系統(tǒng)

本文轉(zhuǎn)載自:

1.Android輸入系統(tǒng)

??說到屏幕點(diǎn)擊事件喧枷,大部分同學(xué)最早想到的就是自定義中點(diǎn)擊事件的處理過程弯院,屏幕滑動Down事件栗菜,Up事件等。自定義View的事件處理其實(shí)在整個(gè)Android輸入系統(tǒng)中只能算是最上層的衬鱼。 輸入系統(tǒng)如果按結(jié)構(gòu)分層可以分為:

input01.PNG
  • 輸入系統(tǒng)部分

??包含輸入子系統(tǒng)以及InputManagerService宙橱,用于事件的捕獲以及分發(fā)給上一級。

  • WindowManagerService處理部分

??輸入系統(tǒng)部分將事件分發(fā)給對應(yīng)的Window柬祠,而Window正是由WMS來管理的北戏。

  • View處理部分

??就是Activity->ViewGroup->View層面的事件分發(fā)的邏輯。

??這里再提供一張更加詳細(xì)的Android輸入系統(tǒng)模型:

input02.PNG

??輸入系統(tǒng)說白了就是捕獲事件漫蛔,并將事件分發(fā)給WMS進(jìn)行處理嗜愈。關(guān)鍵字:事件捕獲旧蛾,事件分發(fā)。

1.1 輸入子系統(tǒng)

??Android中的輸入設(shè)備有很多種蠕嫁,如:鍵盤锨天,屏幕鼠標(biāo)等剃毒,開發(fā)中最常見的就是屏幕和按鍵(如Home鍵等屬于鍵盤)了绍绘。

??這些設(shè)備對于核心處理器來說就是一個(gè)“即插即用”外設(shè),和我們電腦插入鼠標(biāo)或手機(jī)后迟赃,在“設(shè)備管理器”里面會新增一個(gè)輸入設(shè)備節(jié)點(diǎn)一樣(前提是已經(jīng)安裝好對應(yīng)的驅(qū)動) 陪拘。

input03.png

??Android處理器在接入這些“外設(shè)”后,比如滑動屏幕纤壁,設(shè)備驅(qū)動層就會接受到原始事件最終將事件傳遞到用戶空間的設(shè)備節(jié)點(diǎn)(dev/input/)中左刽。Android提供了一些api可以讓開發(fā)者在設(shè)備節(jié)點(diǎn)(dev/input/)中讀取內(nèi)核寫入的事件。

1.2 InputManagerService

??InputManagerService(輸入管理服務(wù))簡稱IMS酌媒,在安卓系統(tǒng)中負(fù)責(zé)它管理整個(gè)系統(tǒng)的輸入部分欠痴,包括鍵盤、鼠標(biāo)秒咨、觸摸屏等等喇辽,它與WindowManager密切相關(guān)。它讀取設(shè)備節(jié)點(diǎn)(dev/input/)中的輸入事件雨席,并對數(shù)據(jù)進(jìn)行二次甚至三次加工后分發(fā)給合適的Window進(jìn)行處理菩咨。IMS整體啟動過程和重要方法如下圖所示。

input04.png

2.事件讀取機(jī)制

??我們以IMS為入口陡厘。在設(shè)備開機(jī)過程中抽米,啟動SystemServer部分初始了很多系統(tǒng)服務(wù),這里面就包括了IMS的創(chuàng)建和啟動過程糙置。 注:文章使用的源碼版本為8.0云茸。以上就是整個(gè)輸入事件讀取的過程:

input05.png

2.1 IMS的創(chuàng)建與啟動

??在SystemServer的run方法中調(diào)用了startOtherServices。

//frameworks/base/services/java/com/android/server/SystemServer.java
private void startOtherServices() {
    ...
    inputManager = new InputManagerService(context);//1.創(chuàng)建IMS
    ...
    wm = WindowManagerService.main(context, inputManager,
                    mFactoryTestMode != FactoryTest.FACTORY_TEST_LOW_LEVEL,
                    !mFirstBoot, mOnlyCore, new PhoneWindowManager());//2.IMS傳入WMS
    ...
    inputManager.start();//3.啟動IMS
}

注釋1:調(diào)用了IMS的構(gòu)造方法谤饭;
注釋2:將IMS作為參數(shù)傳遞給WMS标捺;
注釋3:調(diào)用了IMS的啟動方法start。

??我們來具體分析下注釋1和注釋3:注釋1

public InputManagerService(Context context) {
    ...
    mPtr = nativeInit(this, mContext, mHandler.getLooper().getQueue());
    ...
}

重點(diǎn)看nativeInit方法揉抵,這是一個(gè)native方法亡容。

//frameworks\base\services\core\jni\com_android_server_input_InputManagerService.cpp
static jlong nativeInit(JNIEnv* env, jclass /* clazz */,
        jobject serviceObj, jobject contextObj, jobject messageQueueObj) {
    ...
    NativeInputManager* im = new NativeInputManager(contextObj, serviceObj,
            messageQueue->getLooper());
    im->incStrong(0);
    return reinterpret_cast<jlong>(im);//返回NativeInputManager指針到j(luò)ava framework層
}
//------------------------------------
NativeInputManager::NativeInputManager(jobject contextObj,
        jobject serviceObj, const sp<Looper>& looper) :
        mLooper(looper), mInteractive(true) {
    JNIEnv* env = jniEnv();

    mContextObj = env->NewGlobalRef(contextObj);//1.將java層的傳下來的Context上下文保存在mContextObj中
    mServiceObj = env->NewGlobalRef(serviceObj);//2.將java層傳遞下來的InputManagerService對象保存在mServiceObj中
    ...
    sp<EventHub> eventHub = new EventHub();//3.創(chuàng)建EventHub
    mInputManager = new InputManager(eventHub, this, this);//4.創(chuàng)建InputManager
}

nativeInit方法中創(chuàng)建了一個(gè)NativeInputManager對象,并將該對象指針返回給了java framework層功舀。 這就是為了打通java和native層萍倡,下次需要使用native層的NativeInputManager對象的時(shí)候,直接傳遞這個(gè)指針就可以訪問了辟汰。

??繼續(xù)看NativeInputManager構(gòu)造方法:

  • 注釋1:將java層的傳下來的Context上下文保存在mContextObj中列敲;
  • 注釋2:將java層傳遞下來的InputManagerService對象保存在mServiceObj中阱佛。

??如果你對源碼比較熟悉,可以知道大部分native層和java層的交互都是通過這個(gè)模式戴而,調(diào)用模型圖如下:

input06.png

NativeInputManager構(gòu)造方法注釋3處:創(chuàng)建一個(gè)EventHub凑术,EventHub通過Linux內(nèi)核的INotify和epoll機(jī)制監(jiān)聽設(shè)備節(jié)點(diǎn),使用它的getEvent函數(shù)就可以讀取設(shè)備節(jié)點(diǎn)的原始事件以及設(shè)備節(jié)點(diǎn)增刪事件所意。 這里說明下原始事件和設(shè)備增刪事件:

  • 原始事件:比如鍵盤節(jié)點(diǎn)的輸入事件或者屏幕的觸屏DOWN或者UP事件等淮逊,都屬于原始事件。

  • 設(shè)備增刪事件:值鍵盤或者屏幕等節(jié)點(diǎn)的增刪扶踊,EventHub在獲取這類事件后泄鹏,會在native層創(chuàng)建對應(yīng)的節(jié)點(diǎn)處理Mapper對象。

NativeInputManager構(gòu)造方法注釋4處:創(chuàng)建一個(gè)InputManager對象并將注釋3處的EventHub對象作為參數(shù)傳入秧耗。 進(jìn)入InputManager構(gòu)造方法:

InputManager::InputManager(
        const sp<EventHubInterface>& eventHub,
        const sp<InputReaderPolicyInterface>& readerPolicy,
        const sp<InputDispatcherPolicyInterface>& dispatcherPolicy) {
    mDispatcher = new InputDispatcher(dispatcherPolicy);//1
    mReader = new InputReader(eventHub, readerPolicy, mDispatcher);//2
    initialize();//3
}
//----------------------------------------
void InputManager::initialize() {
    mReaderThread = new InputReaderThread(mReader);
    mDispatcherThread = new InputDispatcherThread(mDispatcher);
}

InputManager構(gòu)造方法中:

  1. 創(chuàng)建InputDispatcher類對象mDispatcher备籽,InputDispatcher類主要用來對原始事件進(jìn)行分發(fā),傳遞給WMS分井;

  2. 創(chuàng)建InputReader類對象mReader车猬,并傳入1中mDispatcher對象以及eventHub對象, 為什么要傳入這2個(gè)對象呢尺锚?因?yàn)镮nputReader機(jī)制就是:eventHub對象用來讀取事件數(shù)據(jù)珠闰,mDispatcher對象用來將讀取到事件數(shù)據(jù)分發(fā);

  3. initialize方法中創(chuàng)建了InputReaderThread對象和InputDispatcherThread對象 因?yàn)槭录x取機(jī)制是一個(gè)耗時(shí)過程瘫辩,不能在主線程中進(jìn)行伏嗜,所以使用InputReaderThread線程來讀取事件,用InputDispatcherThread線程來分發(fā)事件杭朱。

??關(guān)于IMS的構(gòu)造方法就講了這么多阅仔,先來小結(jié)下:

  1. IMS構(gòu)造方法中:創(chuàng)建了一個(gè)NativeInputManager的native對象吹散,并將java層的Context上下文保存在native層的mContextObj弧械,將java層的IMS對象保存在native層的mServiceObj中 創(chuàng)建InputManager對象并傳入一個(gè)新建的EventHub對象,用于讀取設(shè)備節(jié)點(diǎn)事件空民。

  2. InputManager構(gòu)造方法中:創(chuàng)建了一個(gè)InputDispatcher和InputReader對象刃唐,以及用于讀取事件的InputReaderThread線程和分發(fā)事件的InputDispatcherThread線程。

??下面我們繼續(xù)看IMS的啟動方法界轩,在startOtherServices方法的調(diào)用inputManager.start画饥。

//frameworks/base/services/java/com/android/server/SystemServer.java
private void startOtherServices() {
    ...
    inputManager = new InputManagerService(context);//1.創(chuàng)建IMS
    ...
    wm = WindowManagerService.main(context, inputManager,
                    mFactoryTestMode != FactoryTest.FACTORY_TEST_LOW_LEVEL,
                    !mFirstBoot, mOnlyCore, new PhoneWindowManager());//2.IMS傳入WMS
    ...
    inputManager.start();//3.啟動IMS
}

start方法中主要調(diào)用了nativeStart方法,參數(shù)為初始化時(shí)浊猾,native返回的NativeInputManager對象地址抖甘。

static void nativeStart(JNIEnv* env, jclass /* clazz */, jlong ptr) {
    NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);

    status_t result = im->getInputManager()->start();
    if (result) {
        jniThrowRuntimeException(env, "Input manager could not be started.");
    }

}

nativeStart方法調(diào)用了NativeInputManager的InputManager的start方法。

status_t InputManager::start() {
    status_t result = mDispatcherThread->run("InputDispatcher", PRIORITY_URGENT_DISPLAY);
    ...
    result = mReaderThread->run("InputReader", PRIORITY_URGENT_DISPLAY);
    if (result) {
        ALOGE("Could not start InputReader thread due to error %d.", result);

        mDispatcherThread->requestExit();
        return result;
    }

    return OK;

}

start方法主要作用就是啟動初始化時(shí)創(chuàng)建的兩個(gè)線程:mDispatcherThread和mReaderThread葫慎,這里注意先后順序先啟動事件分發(fā)線程衔彻,再啟動事件讀取線程薇宠。這是為了在事件讀取后可以立即對事件進(jìn)行分發(fā)。

??IMS啟動時(shí)序圖如下:

input07.png

2.2 線程的創(chuàng)建與啟動

??在分析兩個(gè)線程啟動過程之前艰额,我們先來講解下Thread的run方法澄港。

//system\core\libutils\Threads.h
status_t Thread::run(const char* name, int32_t priority, size_t stack)
{
    ...
    res = createThreadEtc(_threadLoop,
                this, name, priority, stack, &mThread);
    ...
}

Thread的run方法中調(diào)用了createThreadEtc,這個(gè)方法第一個(gè)參數(shù)_threadLoop是一個(gè)方法指針柄沮,第二個(gè)參數(shù)是自己回梧,最終會調(diào)用到_threadLoop方法并傳入this指針。

int Thread::_threadLoop(void* user)
    Thread* const self = static_cast<Thread*>(user);
    ...
    do {
        bool result;
        result = self->threadLoop();
        ...
        if (result == false || self->mExitPending) {
            ...
            break;
        }
        ...
    } while(strong != 0);
}

_threadLoop方法內(nèi)部會調(diào)用self->threadLoop()祖搓,這個(gè)self就是當(dāng)前Thread的this指針狱意,除了result返回false或者調(diào)用了requestExit才會退出。 不然會一直循環(huán)調(diào)用self->threadLoop()函數(shù)拯欧,threadLoop在Thread中是一個(gè)純虛函數(shù)髓涯,在其子類中實(shí)現(xiàn)。

2.2.1 InputDispatcher/InputDispatcherThread

??所以后面只要分析子線程的threadLoop方法即可哈扮,下面我們先來分析InputDispatcherThread:

bool InputDispatcherThread::threadLoop() {
    mDispatcher->dispatchOnce();
    return true;
}

這里的mDispatcher是InputDispatcher類對象纬纪。

void InputDispatcher::dispatchOnce() {
    nsecs_t nextWakeupTime = LONG_LONG_MAX;
    { // acquire lock
        ..
        if (!haveCommandsLocked()) {//1.判斷有沒有命令需要執(zhí)行
            dispatchOnceInnerLocked(&nextWakeupTime);//2.分發(fā)事件
        }

        if (runCommandsLockedInterruptible()) {//3.執(zhí)行命令
            nextWakeupTime = LONG_LONG_MIN;//4.設(shè)置下次喚醒的時(shí)間
        }
    } // release lock
    ...
    mLooper->pollOnce(timeoutMillis);//5.讓線程進(jìn)入休眠

}

注釋1:判斷有沒有命令需要執(zhí)行,如果沒有命令滑肉,則調(diào)用注釋2的dispatchOnceInnerLocked分發(fā)事件包各;如果有命令,則在注釋3處執(zhí)行完所有命令后靶庙,將nextWakeupTime置為LONG_LONG_MIN问畅,這樣就可以讓下一次線程可以被立即喚醒。 注釋5處調(diào)用mLooper->pollOnce六荒,讓線程進(jìn)入休眠护姆。第一次啟動的時(shí)候是直接進(jìn)入休眠,等待事件的到來掏击。

2.2.2 InputReader/InputReaderThread

??下面再來分析事件讀取線程InputReaderThread:

bool InputReaderThread::threadLoop() {
    mReader->loopOnce();
    return true;
}

這里mReader是InputReader類對象:

void InputReader::loopOnce() {
    ...
    size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);//1.獲取事件

    { // acquire lock
        ...
        if (count) {
            processEventsLocked(mEventBuffer, count);//2.處理事件
        }
        ...
    } // release lock

    ...
    mQueuedListener->flush();//3

}

2.3 事件的獲取與賦值

  • 注釋1處調(diào)用EventHub的getEvents方法讀取設(shè)備節(jié)點(diǎn)中的輸入事件卵皂,注意這個(gè)方法內(nèi)部在沒有輸入事件的時(shí)候也是一個(gè)休眠的過程,并不是死循環(huán)耗時(shí)操作砚亭。

  • 在注釋2處如果count不為0灯变,說明有事件,調(diào)用processEventsLocked方法捅膘。

2.4 事件的處理與加工

??進(jìn)入processEventsLocked看看:

void InputReader::processEventsLocked(const RawEvent* rawEvents, size_t count) {
    for (const RawEvent* rawEvent = rawEvents; count;) {
        int32_t type = rawEvent->type;

        if (type < EventHubInterface::FIRST_SYNTHETIC_EVENT) {//1.原始事件類型
            ...
            processEventsForDeviceLocked(deviceId, rawEvent, batchSize);
        } else {//2.設(shè)備節(jié)點(diǎn)事件
            switch (rawEvent->type) {
            case EventHubInterface::DEVICE_ADDED:
                addDeviceLocked(rawEvent->when, rawEvent->deviceId);//添加設(shè)備
                break;
            case EventHubInterface::DEVICE_REMOVED:
                removeDeviceLocked(rawEvent->when, rawEvent->deviceId);//刪除設(shè)備
                break;
            case EventHubInterface::FINISHED_DEVICE_SCAN:
                handleConfigurationChangedLocked(rawEvent->when);//設(shè)備配置改變
                break;
            default:
                ALOG_ASSERT(false); // can't happen
                break;
            }
        }
        count -= batchSize;
    }
}

processEventsLocked方法主要實(shí)現(xiàn)了:根據(jù)事件的type類型進(jìn)行不同處理添祸。

  • 原始事件:調(diào)用processEventsForDeviceLocked處理。

  • 設(shè)備節(jié)點(diǎn)事件:調(diào)用節(jié)點(diǎn)的添加和刪除等操作寻仗。

??我們先來看原始事件:processEventsForDeviceLocked

void InputReader::processEventsForDeviceLocked(int32_t deviceId,
        const RawEvent* rawEvents, size_t count) {
    ssize_t deviceIndex = mDevices.indexOfKey(deviceId);//獲取設(shè)備在mDevices中的索引
    ...
    InputDevice* device = mDevices.valueAt(deviceIndex);//根據(jù)索引獲取InputDevice對象
    ...
    device->process(rawEvents, count);//調(diào)用InputDevice的process方法繼續(xù)處理
}

根據(jù)deviceId去獲取設(shè)備在mDevices中的索引刃泌,根據(jù)索引獲取InputDevice對象。然后調(diào)用InputDevice的process方法繼續(xù)處理。

void InputDevice::process(const RawEvent* rawEvents, size_t count) {
    ...
    size_t numMappers = mMappers.size();
    for (const RawEvent* rawEvent = rawEvents; count--; rawEvent++) {
        ...
            ...
            for (size_t i = 0; i < numMappers; i++) {
                InputMapper* mapper = mMappers[i];
                mapper->process(rawEvent);
            }
            ...
        ... 
    }   
}

process方法最終是使用不同的InputMapper進(jìn)行處理耙替, 那這個(gè)InputMapper在哪里設(shè)置成員呢鲤遥。

??我們回到前面processEventsLocked方法:如果是節(jié)點(diǎn)處理事件,如添加則調(diào)用addDeviceLocked方法, addDeviceLocked方法中又調(diào)用了createDeviceLocked方法林艘,并將返回的InputDevice放入到mDevices列表中盖奈。

void InputReader::addDeviceLocked(nsecs_t when, int32_t deviceId) {
    ...
    InputDevice* device = createDeviceLocked(deviceId, controllerNumber, identifier, classes);
    ...
    mDevices.add(deviceId, device);
    ...
}
//--------------------------------------
InputDevice* InputReader::createDeviceLocked(int32_t deviceId, int32_t controllerNumber,
        const InputDeviceIdentifier& identifier, uint32_t classes) {
    InputDevice* device = new InputDevice(&mContext, deviceId, bumpGenerationLocked(),
            controllerNumber, identifier, classes);

    ...
    // Switch-like devices.
    if (classes & INPUT_DEVICE_CLASS_SWITCH) {
        device->addMapper(new SwitchInputMapper(device));
    }

    // Scroll wheel-like devices.
    if (classes & INPUT_DEVICE_CLASS_ROTARY_ENCODER) {
        device->addMapper(new RotaryEncoderInputMapper(device));
    }
    ...
    // Keyboard-like devices.
    ...
    if (keyboardSource != 0) {
        device->addMapper(new KeyboardInputMapper(device, keyboardSource, keyboardType));
    }
    ...
    // Touchscreens and touchpad devices.
    if (classes & INPUT_DEVICE_CLASS_TOUCH_MT) {
        device->addMapper(new MultiTouchInputMapper(device));
    } else if (classes & INPUT_DEVICE_CLASS_TOUCH) {
        device->addMapper(new SingleTouchInputMapper(device));
    }
    ...
    return device;

}

可以看到createDeviceLocked根據(jù)不同輸入類型給Device設(shè)備添加了不同的InputMapper。

??回到前面InputDevice::process方法中:這里用KeyboardInputMapper來做例子狐援。 這個(gè)方法調(diào)用了KeyboardInputMapper的process方法钢坦。

void KeyboardInputMapper::process(const RawEvent* rawEvent) {
    switch (rawEvent->type) {
        case EV_KEY: {
            ..
            if (isKeyboardOrGamepadKey(scanCode)) {
                processKey(rawEvent->when, rawEvent->value != 0, scanCode, usageCode);
            }
            break;
        }
    }
}
//--------------------------------------
void KeyboardInputMapper::processKey(nsecs_t when, bool down, int32_t scanCode,
        int32_t usageCode) {

    ...
    NotifyKeyArgs args(when, getDeviceId(), mSource, policyFlags,
            down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP,
            AKEY_EVENT_FLAG_FROM_SYSTEM, keyCode, scanCode, keyMetaState, downTime);
    getListener()->notifyKey(&args);

}

最終在processKey方法中將原始事件封裝為一個(gè)新的NotifyKeyArgs對象,并調(diào)用 getListener()->notifyKey方法作為參數(shù)傳入啥酱,這里getListener是在InputReader構(gòu)造方法中初始化爹凹。

InputReader::InputReader(const sp<EventHubInterface>& eventHub,
        const sp<InputReaderPolicyInterface>& policy,
        const sp<InputListenerInterface>& listener) :
        mContext(this), mEventHub(eventHub), mPolicy(policy),
        mGlobalMetaState(0), mGeneration(1),
        mDisableVirtualKeysTimeout(LLONG_MIN), mNextTimeout(LLONG_MAX),
        mConfigurationChangesToRefresh(0) {
    mQueuedListener = new QueuedInputListener(listener);
    ...
}

就是這個(gè)mQueuedListener,而這個(gè)listener是外部傳入的InputDispatcher對象镶殷。

??那進(jìn)入QueuedInputListener的notifyKey看看:

void QueuedInputListener::notifyKey(const NotifyKeyArgs* args) {
    mArgsQueue.push(new NotifyKeyArgs(*args));
}

這里只是將NotifyKeyArgs事件對象存儲到mArgsQueue隊(duì)列中禾酱,并沒有真正對事件進(jìn)行處理,那什么時(shí)候處理呢绘趋? 觀察仔細(xì)的同學(xué)應(yīng)該看到在InputReader的loopOnce中調(diào)用了mQueuedListener->flush()颤陶。

void InputReader::loopOnce() {
    ...
    mQueuedListener->flush();//3
}
//---------------------------------
void QueuedInputListener::flush() {
    size_t count = mArgsQueue.size();
    for (size_t i = 0; i < count; i++) {
        NotifyArgs* args = mArgsQueue[i];
        args->notify(mInnerListener);
        delete args;
    }
    mArgsQueue.clear();
}

這個(gè)flush方法就是循環(huán)調(diào)用列表中的事件,并依次執(zhí)行NotifyArgs的notify方法傳入的參數(shù)mInnerListener為初始化時(shí)的InputDispatcher對象陷遮。

2.5 事件的分發(fā)與通知

??繼續(xù)進(jìn)入NotifyArgs的notify方法:

struct NotifyArgs {
    virtual void notify(const sp<InputListenerInterface>& listener) const = 0;
}

notify方法是一個(gè)純虛函數(shù)滓走,由其子類實(shí)現(xiàn)。這里子類就是NotifyKeyArgs對象帽馋。

void NotifyKeyArgs::notify(const sp<InputListenerInterface>& listener) const {
    listener->notifyKey(this);
}

這里listener是InputDispatcher的對象搅方。

2.5.1 InputDispatcher/InputDispatcherThread

void InputDispatcher::notifyKey(const NotifyKeyArgs* args) {
    ...
    bool needWake;
    { // acquire lock
        ...
        KeyEntry* newEntry = new KeyEntry(args->eventTime,
                args->deviceId, args->source, policyFlags,
                args->action, flags, keyCode, args->scanCode,
                metaState, repeatCount, args->downTime);//1.封裝成KeyEntry

        needWake = enqueueInboundEventLocked(newEntry); //2.將KeyEntry對象壓入mInboundQueue中
    } // release lock

    if (needWake) {
        mLooper->wake();//3.喚醒InputDispatcherThread線程進(jìn)行處理
    }

}
  • 注釋1處:將NotifyKeyArgs事件重新封裝為一個(gè)KeyEntry對象。

  • 注釋2處:enqueueInboundEventLocked將KeyEntry對象壓入mInboundQueue中绽族。

  • 注釋3處:喚醒InputDispatcherThread線程進(jìn)行處理姨涡。

??到這里InputReader的輸入事件讀取流程已經(jīng)全部走完。

??事件讀取時(shí)序圖如下:

input08.png

2.6 事件獲取流程小結(jié)

  1. SystemServer創(chuàng)建并啟動InputManagerService吧慢;

  2. InputManagerService在native層創(chuàng)建一個(gè)NativeInputManager對象涛漂;

  3. NativeInputManager內(nèi)部創(chuàng)建一個(gè)InputManager對象;

  4. InputManager啟動InputReaderThread和InputDispatcherThread娄蔼;

  5. 在InputReaderThread線程中調(diào)用EventHub的getEvents獲取設(shè)備節(jié)點(diǎn)中的輸入事件怖喻;

  6. 并將輸入事件封裝為NotifyKeyArgs對象放入隊(duì)列中;

  7. 之后再調(diào)用flush岁诉,依次將事件傳遞給InputDispatcher;

  8. InputDispatcher在收到事件后跋选,會重新封裝為一個(gè)KeyEntry對象涕癣,并壓入mInboundQueue列表中;

  9. 最后喚醒InputDispatcherThread線程。

3.事件分發(fā)機(jī)制

??為了讓大家不會迷失在源碼中坠韩,筆者先拋出幾個(gè)問題距潘,然后帶著問題去看源碼。

  • 問題1:窗口什么時(shí)候傳入IMS輸入系統(tǒng)的只搁?IMS是如何找到對應(yīng)的Window窗口的音比?
  • 問題2:IMS和WMS是通過什么方式通訊將事件分發(fā)出去的?

??這里再提前放一張圖讓大家對輸入事件模型有個(gè)概念氢惋,然后再結(jié)合源碼去分析洞翩。

input09.png

3.1 事件分發(fā)入口確定

??前面講到事件讀取流程將事件放入封裝為KeyEntry放入到mInboundQueue隊(duì)列的尾部tail,并喚醒InputDispatcherThread線程焰望,就以這里為入口骚亿。

void InputDispatcher::notifyKey(const NotifyKeyArgs* args) {
    ...
    bool needWake;
    { // acquire lock
        ...
        KeyEntry* newEntry = new KeyEntry(args->eventTime,
                args->deviceId, args->source, policyFlags,
                args->action, flags, keyCode, args->scanCode,
                metaState, repeatCount, args->downTime);//1.封裝成KeyEntry

        needWake = enqueueInboundEventLocked(newEntry); //2.將KeyEntry對象壓入mInboundQueue中
    } // release lock

    if (needWake) {
        mLooper->wake();//3.喚醒InputDispatcherThread線程進(jìn)行處理
    }

}
//---------------------------------------------
bool InputDispatcher::enqueueInboundEventLocked(EventEntry* entry) {
    ...
    mInboundQueue.enqueueAtTail(entry);// 將entry加入到隊(duì)尾
}

3.2 事件分發(fā)線程喚醒

??進(jìn)入InputDispatcherThread的threadLoop方法: 關(guān)于Thread的threadLoop方法解釋已經(jīng)在捕捉輸入事件時(shí)講過,這里不再重復(fù)熊赖。

bool InputDispatcherThread::threadLoop() {
    mDispatcher->dispatchOnce();//mDispatcher 是InputDispatcher類型對象
    return true;
}
//-----------------------------------------
void InputDispatcher::dispatchOnce() {  
    ...
    if (!haveCommandsLocked()) {//1.判斷是否有指令需要執(zhí)行
        dispatchOnceInnerLocked(&nextWakeupTime);//處理輸入事件
    }
    ...
    if (runCommandsLockedInterruptible()) {//2.執(zhí)行命令
        nextWakeupTime = LONG_LONG_MIN;
    }
    mLooper->pollOnce(timeoutMillis);
}

注釋1處判斷是否有指令需要執(zhí)行来屠,如果沒有,則調(diào)用dispatchOnceInnerLocked去處理輸入事件震鹉,如果有俱笛,則優(yōu)先調(diào)用runCommandsLockedInterruptible處理事件, 為了讓線程可以立即進(jìn)入事件處理传趾,將nextWakeupTime 設(shè)置為LONG_LONG_MIN嫂粟,這樣線程在指令執(zhí)行完畢后可以立即被喚醒去處理輸入事件。

??從這里可以看出dispatchOnce主要是做了兩個(gè)功能:1.執(zhí)行指令 2.處理輸入事件墨缘,且指令執(zhí)行優(yōu)先級高于輸入事件處理星虹。 這里的指令例如:對waitQueue中的事件進(jìn)行出棧,后面會講到镊讼。

??進(jìn)入注釋2輸入事件處理:dispatchOnceInnerLocked

void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) {
    switch (mPendingEvent->type) {
    ...
    case EventEntry::TYPE_KEY: {// 按鍵事件類型
        KeyEntry* typedEntry = static_cast<KeyEntry*>(mPendingEvent);
        ...
        done = dispatchKeyLocked(currentTime, typedEntry, &dropReason, nextWakeupTime);
        break;
    }

    case EventEntry::TYPE_MOTION: {// 移動事件類型
        MotionEntry* typedEntry = static_cast<MotionEntry*>(mPendingEvent);
        ...
        done = dispatchMotionLocked(currentTime, typedEntry,
                &dropReason, nextWakeupTime);
        break;
    }
    if (done) {
        ...
        releasePendingEventLocked(); // 事件處理分發(fā)完畢后宽涌,用于釋放對應(yīng)的資源
        *nextWakeupTime = LONG_LONG_MIN;  // force next poll to wake up immediately
    }

}

dispatchOnceInnerLocked方法主要是根據(jù)事件的類型調(diào)用不同的處理方法。 我們拿TYPE_KEY 按鍵事件來作為主線蝶棋,關(guān)于TYPE_MOTION觸摸事件處理邏輯都是差不過的卸亮,感興趣的同學(xué)可以自行閱讀源碼。 在事件處理分發(fā)完畢后會調(diào)用releasePendingEventLocked里面會釋放對應(yīng)的內(nèi)存資源

??mPendingEvent代表當(dāng)前需要處理的輸入事件,傳遞給dispatchKeyLocked去處理。

bool InputDispatcher::dispatchKeyLocked(nsecs_t currentTime, KeyEntry* entry,
        DropReason* dropReason, nsecs_t* nextWakeupTime) {
    ...
    Vector<InputTarget> inputTargets;
    int32_t injectionResult = findFocusedWindowTargetsLocked(currentTime,
            entry, inputTargets, nextWakeupTime); //1.找到焦點(diǎn)窗口
    if (injectionResult == INPUT_EVENT_INJECTION_PENDING) {
        return false;
    }
    ...
    addMonitoringTargetsLocked(inputTargets);//2.放入一個(gè)監(jiān)聽input通道

    // Dispatch the key.
    dispatchEventLocked(currentTime, entry, inputTargets);  //3.實(shí)際處理事件 處

}

dispatchKeyLocked在注釋1處獲取按鍵事件對應(yīng)的Window窗口勘纯,在注釋2處放入一個(gè)監(jiān)聽input通道笔咽,注釋3處實(shí)際處理事件處。

??在講解注釋1處獲取窗口邏輯前我們先來看下我們窗口是如何傳入到InputDispatcher對象中的以及InputChannel概念糠排。

3.3 事件分發(fā)通道注冊

??我們Window是在ViewRootImpl的setView方法中傳入WMS的。

public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
    ...
    mInputChannel = new InputChannel();//創(chuàng)建通道
    res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
                            getHostVisibility(), mDisplay.getDisplayId(),
                            mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
                            mAttachInfo.mOutsets, mInputChannel);
    if (mInputChannel != null) {
        mInputEventReceiver = new       WindowInputEventReceiver(mInputChannel,Looper.myLooper());
    }
}

mWindowSession是IWindowSession在app端的代理對象。實(shí)際執(zhí)行的是Session類:

//frameworks\base\services\core\java\com\android\server\wm\Session.java
public int addToDisplay(IWindow window,...InputChannel outInputChannel) {
    return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId,
            outContentInsets, outStableInsets, outOutsets, outInputChannel);
}

這里的mService是WMS對象螺垢,重點(diǎn)記住最后一個(gè)參數(shù)outInputChannel。

//WMS:
public int addWindow(Session session, IWindow client...InputChannel outInputChannel) {
    ...
    final WindowState win = new WindowState(this, session, client, token, parentWindow,
                    appOp[0], seq, attrs, viewVisibility, session.mUid,
                    session.mCanAddInternalSystemWindow);
    ...
    win.openInputChannel(outInputChannel);  //1.打開InputChannel通道
    if (focusChanged) {
        mInputMonitor.setInputFocusLw(mCurrentFocus, false /*updateInputWindows*/);//2
    }
}

addWindow的注釋1處調(diào)用WindowState打開InputChannel通道,什么是InputChannel通道呢枉圃?

??進(jìn)入WindowState的openInputChannel看看:

void openInputChannel(InputChannel outInputChannel) {
    ...
    String name = getName();
    InputChannel[] inputChannels = InputChannel.openInputChannelPair(name);//1
    mInputChannel = inputChannels[0];//socket服務(wù)端的InputChannel
    mClientChannel = inputChannels[1];//socket客戶端的InputChannel
    mInputWindowHandle.inputChannel = inputChannels[0];
    if (outInputChannel != null) {
        mClientChannel.transferTo(outInputChannel);//將
        mClientChannel.dispose();
        mClientChannel = null;
    }
    ...
    mService.mInputManager.registerInputChannel(mInputChannel, mInputWindowHandle);//2
}
//--------------------------------------------------
//InputChannel.java:
public static InputChannel[] openInputChannelPair(String name) {
    ...
    return nativeOpenInputChannelPair(name);
}
//--------------------------------------------------
//android_view_InputChannel.cpp:
static jobjectArray android_view_InputChannel_nativeOpenInputChannelPair(JNIEnv* env...) {
    ...
    sp<InputChannel> serverChannel;
    sp<InputChannel> clientChannel;
    status_t result = InputChannel::openInputChannelPair(name, serverChannel, clientChannel);

    jobjectArray channelPair = env->NewObjectArray(2, gInputChannelClassInfo.clazz, NULL);
    jobject serverChannelObj = android_view_InputChannel_createInputChannel(env,
            std::make_unique<NativeInputChannel>(serverChannel));
    jobject clientChannelObj = android_view_InputChannel_createInputChannel(env,
            std::make_unique<NativeInputChannel>(clientChannel));
    ...
    env->SetObjectArrayElement(channelPair, 0, serverChannelObj);
    env->SetObjectArrayElement(channelPair, 1, clientChannelObj);
    return channelPair;

}
//--------------------------------------------------
status_t InputChannel::openInputChannelPair(const String8& name,
        sp<InputChannel>& outServerChannel, sp<InputChannel>& outClientChannel) {
    int sockets[2];
    if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sockets)) {
        ...
        return result;
    }
    ...
    outServerChannel = new InputChannel(serverChannelName, sockets[0]);
    outClientChannel = new InputChannel(clientChannelName, sockets[1]);
    return OK;
}

通過以上代碼可以看出InputChannel使用的是sockets通訊功茴,且WindowState的openInputChannel中注釋1處:InputChannel[] inputChannels = InputChannel.openInputChannelPair(name),返回的inputChannels是一個(gè)服務(wù)端和客戶端的輸入通道數(shù)組孽亲,其中: 下標(biāo)0:表示服務(wù)端的InputChannel;下標(biāo)1:表示客戶端的InputChannel坎穿。

??有了這些基礎(chǔ)我們再來細(xì)細(xì)品味下這段代碼:為什么registerInputChannel傳遞的是mInputChannel(socket服務(wù)端)而不是mClientChannel(socket客戶端)。

void openInputChannel(InputChannel outInputChannel) {
    ...
    String name = getName();
    InputChannel[] inputChannels = InputChannel.openInputChannelPair(name);//1
    mInputChannel = inputChannels[0];//socket服務(wù)端的InputChannel
    mClientChannel = inputChannels[1];//socket客戶端的InputChannel
    mInputWindowHandle.inputChannel = inputChannels[0];
    if (outInputChannel != null) {
        // app的outInputChannel和socket的客戶端mClientChannel關(guān)聯(lián)
        mClientChannel.transferTo(outInputChannel);//outInputChannel:app層傳遞進(jìn)來的InputChannel
        mClientChannel.dispose();
        mClientChannel = null;
    }
    ...
    mService.mInputManager.registerInputChannel(mInputChannel, mInputWindowHandle);//2
}

通過前面分析知:

  • mInputChannel:socket服務(wù)端的InputChannel返劲;

  • mClientChannel:socket客戶端的InputChannel玲昧;

  • outInputChannel:app層傳遞進(jìn)來的InputChannel。

3.3.1 InputChannel創(chuàng)建流程

3.3.2 Channel模型

  1. 調(diào)用mService.mInputManager.registerInputChannel

??將wms在socket服務(wù)端的InputChannel(mInputChannel)注冊到IMS中酌呆。這樣在IMS輸入系統(tǒng)就可以給socket服務(wù)端的InputChannel(mInputChannel)寫入數(shù)據(jù),在WMS的socket客戶端InputChannel(mClientChannel)就可以讀取數(shù)據(jù)搔耕。

  1. 調(diào)用mClientChannel.transferTo(outInputChannel)

??將app端的InputChannel(outInputChannel)和wms的socket客戶端InputChannel(mClientChannel)關(guān)聯(lián)隙袁,這樣就可以向socket客戶端InputChannel(mClientChannel)中寫入數(shù)據(jù)然后通知app端的InputChannel(outInputChannel),實(shí)際傳遞給ViewRootImpl對象處理弃榨,接著就是View層面的處理了菩收。

??這里我們繼續(xù)看注釋2處:mService.mInputManager.registerInputChannel 最終會進(jìn)入native層:

//frameworks\base\services\core\jni\com_android_server_input_InputManagerService.cpp
static void nativeRegisterInputChannel(JNIEnv* env, jclass /* clazz */,
        jlong ptr, jobject inputChannelObj, jobject inputWindowHandleObj, jboolean monitor) {
    NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
    ...
    status_t status = im->registerInputChannel(
            env, inputChannel, inputWindowHandle, monitor);//1.socket的服務(wù)端

}
frameworks/base/services/core/jni/com_android_server_input_InputManagerService.cpp
status_t NativeInputManager::registerInputChannel(JNIEnv* /* env */,
        const sp<InputChannel>& inputChannel,
        const sp<InputWindowHandle>& inputWindowHandle, bool monitor) {
    return mInputManager->getDispatcher()->registerInputChannel(
            inputChannel, inputWindowHandle, monitor);
}
//----------------------------------------------
//frameworks/native/services/inputflinger/InputDispatcher.cpp
status_t InputDispatcher::registerInputChannel(const sp<InputChannel>& inputChannel,
        const sp<InputWindowHandle>& inputWindowHandle, bool monitor) {
        ...
        sp<Connection> connection = new Connection(inputChannel, inputWindowHandle, monitor);

        int fd = inputChannel->getFd();
        mConnectionsByFd.add(fd, connection);
        ...

        // registerInputChannel里面?zhèn)魅氲膍onitor是false --> nativeRegisterInputChannel(mPtr, inputChannel, inputWindowHandle, false);
        // 所以這個(gè)流程不會將窗口的channel放到mMonitoringChannels里面
        if (monitor) {
            mMonitoringChannels.push(inputChannel);
        }
        ...

}

im->registerInputChannel參數(shù)說明:

  • inputChannel:WMS在socket服務(wù)端InputChannel(mInputChannel);

  • inputWindowHandle:WMS內(nèi)的一個(gè)包含Window所有信息的實(shí)例鲸睛;

  • monitor:值為false娜饵,表示不加入監(jiān)控。

??InputChannel時(shí)序圖如下:

input10.png

??最后階段在InputDispatcher中創(chuàng)建一個(gè)Connection并加入到mConnectionsByFd隊(duì)列中官辈,key為當(dāng)前inputChannel的fd箱舞。獲取的時(shí)候也是通過inputChannel的fd去獲取。

input11.png

通過上面的分析我們知道了拳亿,和輸入系統(tǒng)InputDispatcher使用的socket通訊晴股,在View端,WMS端以及IMS端都有一個(gè)InputChannel肺魁。

  • View端:outInputChannel(和mClientChannel綁定一起)电湘;

  • WMS端:mClientChannel(socket客戶端);

  • IMS端:mInputChannel(socket服務(wù)端)鹅经。

InputDispatcher -> mInputChannel -> mClientChannel -> outInputChannel -> view

??哪個(gè)部分有數(shù)據(jù)需要分發(fā)就是將數(shù)據(jù)寫入通道中寂呛。 那么接下來我們看看IMS是如何選取對應(yīng)的通道的。

3.4 事件分發(fā)窗口確認(rèn)

??回到InputDispatcher::dispatchKeyLocked方法瘾晃。

bool InputDispatcher::dispatchKeyLocked(nsecs_t currentTime, KeyEntry* entry,
        DropReason* dropReason, nsecs_t* nextWakeupTime) {
    ..
    int32_t injectionResult = findFocusedWindowTargetsLocked(currentTime,
            entry, inputTargets, nextWakeupTime); //1.找到焦點(diǎn)窗口

    dispatchEventLocked(currentTime, entry, inputTargets);  //2.處理分發(fā)事件

}

進(jìn)入findFocusedWindowTargetsLocked:這個(gè)方法就是用來確認(rèn)當(dāng)前需要傳遞事件的窗口贷痪。 重點(diǎn)看inputTargets的賦值操作。

int32_t InputDispatcher::findFocusedWindowTargetsLocked(...inputTargets){
    ...
    addWindowTargetLocked(mFocusedWindowHandle,
            InputTarget::FLAG_FOREGROUND | InputTarget::FLAG_DISPATCH_AS_IS, BitSet32(0),
            inputTargets);
    ...
}
//----------------------------------------------
void InputDispatcher::addWindowTargetLocked(const sp<InputWindowHandle>& windowHandle,
        int32_t targetFlags, BitSet32 pointerIds, Vector<InputTarget>& inputTargets) {
    inputTargets.push();

    const InputWindowInfo* windowInfo = windowHandle->getInfo();
    InputTarget& target = inputTargets.editTop();
    target.inputChannel = windowInfo->inputChannel;
    target.flags = targetFlags;
    target.xOffset = - windowInfo->frameLeft;
    target.yOffset = - windowInfo->frameTop;
    target.scaleFactor = windowInfo->scaleFactor;
    target.pointerIds = pointerIds;

}

這里可以看出findFocusedWindowTargetsLocked方法中對inputTargets的頭部數(shù)據(jù)進(jìn)行了賦值酗捌,其中將windowInfo->inputChannel通道賦值給了target.inputChannel呢诬。 那么這個(gè)windowInfo是個(gè)什么涌哲?怎么獲扰昼汀尚镰?

??windowInfo是InputWindowHandle的屬性,而InputWindowHandle傳入的是一個(gè)mFocusedWindowHandle對象哪廓。 從名字也可以大概看出這是一個(gè)包含焦點(diǎn)Window信息的對象狗唉。

??那么這個(gè)焦點(diǎn)Window是在哪里賦值的呢?

3.5 事件分發(fā)窗口注冊

??我們回到WMS的addWindow步驟涡真。

// frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java
@Override
public int addWindow(Session session, IWindow client, int seq,
            WindowManager.LayoutParams attrs, int viewVisibility, int displayId,
            Rect outContentInsets, Rect outStableInsets, Rect outOutsets,
            InputChannel outInputChannel) {
    ...
    if (focusChanged) {
        mInputMonitor.setInputFocusLw(mCurrentFocus, false /*updateInputWindows*/);
    }
    mInputMonitor.updateInputWindowsLw(false /*force*/);
    ...
}

在焦點(diǎn)發(fā)生改變的時(shí)候會調(diào)用setInputFocusLw方法和updateInputWindowsLw分俯,updateInputWindowsLw經(jīng)過層層調(diào)用最終會走到InputDispatcher::setInputWindows中。

// frameworks/native/services/inputflinger/InputDispatcher.cpp
void InputDispatcher::setInputWindows(const Vector<sp<InputWindowHandle> >& inputWindowHandles) {
    ...
    mWindowHandles = inputWindowHandles;

    sp<InputWindowHandle> newFocusedWindowHandle;
    ...
    for (size_t i = 0; i < mWindowHandles.size(); i++) {
        const sp<InputWindowHandle>& windowHandle = mWindowHandles.itemAt(i);
        ...
        if (windowHandle->getInfo()->hasFocus) {
            newFocusedWindowHandle = windowHandle;
        }
        ...
        mFocusedWindowHandle = newFocusedWindowHandle;
    }
    ...

}

看到了這里對mWindowHandles和mFocusedWindowHandle做了賦值哆料。

  • mWindowHandles:代表所有Window的Handler對象缸剪;
  • mFocusedWindowHandle:表示焦點(diǎn)Window的Handler對象,通過這些代碼就讓我們IMS中獲取到了需要處理的焦點(diǎn)Window东亦。

??window窗口賦值時(shí)序圖如下:

input12.png

3.6 事件分發(fā)最終處理

??繼續(xù)回到回到InputDispatcher::dispatchKeyLocked方法的注釋2杏节。

dispatchEventLocked(currentTime, entry, inputTargets); //2
void InputDispatcher::dispatchEventLocked(nsecs_t currentTime,
        EventEntry* eventEntry, const Vector<InputTarget>& inputTargets) {
    ...
    for (size_t i = 0; i < inputTargets.size(); i++) {
        const InputTarget& inputTarget = inputTargets.itemAt(i);

        ssize_t connectionIndex = getConnectionIndexLocked(inputTarget.inputChannel);
        if (connectionIndex >= 0) {
            sp<Connection> connection = mConnectionsByFd.valueAt(connectionIndex);
            prepareDispatchCycleLocked(currentTime, connection, eventEntry, &inputTarget);
        }
        ...
    }

}
//-----------------------------------------------------
ssize_t InputDispatcher::getConnectionIndexLocked(const sp<InputChannel>& inputChannel) {
    ssize_t connectionIndex = mConnectionsByFd.indexOfKey(inputChannel->getFd());
    if (connectionIndex >= 0) {
        sp<Connection> connection = mConnectionsByFd.valueAt(connectionIndex);
        if (connection->inputChannel.get() == inputChannel.get()) {
            return connectionIndex;
        }
    }

    return -1;

}

dispatchEventLocked主要作用:輪詢inputTargets,根據(jù)inputTarget.inputChannel獲取其在mConnectionsByFd中的索引典阵,根據(jù)索引獲取Connection對象奋渔,并調(diào)用prepareDispatchCycleLocked進(jìn)行處理。

??prepareDispatchCycleLocked方法內(nèi)部調(diào)用了enqueueDispatchEntriesLocked方法:

void InputDispatcher::enqueueDispatchEntriesLocked(connection,..){
    // Enqueue dispatch entries for the requested modes.
    enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,...);//1
    ...
    // If the outbound queue was previously empty, start the dispatch cycle going.
    if (wasEmpty && !connection->outboundQueue.isEmpty()) {//2
        startDispatchCycleLocked(currentTime, connection);//3
    }
}
//--------------------------------------------
void InputDispatcher::enqueueDispatchEntryLocked(
        const sp<Connection>& connection, EventEntry* eventEntry, const InputTarget* inputTarget,
        int32_t dispatchMode) {
    ...
    DispatchEntry* dispatchEntry = new DispatchEntry(eventEntry,
            inputTargetFlags, inputTarget->xOffset, inputTarget->yOffset,
            inputTarget->scaleFactor);

    switch (eventEntry->type) {
        case EventEntry::TYPE_KEY: {
            KeyEntry* keyEntry = static_cast<KeyEntry*>(eventEntry);
            dispatchEntry->resolvedAction = keyEntry->action;
            dispatchEntry->resolvedFlags = keyEntry->flags;
            ...
            break;
        }
        ...
    }
    ...
    connection->outboundQueue.enqueueAtTail(dispatchEntry);
    ...

}

在注釋1處enqueueDispatchEntryLocked方法中會將輸入事件重新封裝為一個(gè)DispatchEntry并壓入connection的outboundQueue隊(duì)列中壮啊。 然后在注釋2處判斷如果事件不為空嫉鲸,則調(diào)用startDispatchCycleLocked循環(huán)發(fā)送輸入事件。

void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime,
        const sp<Connection>& connection) {
    while (connection->status == Connection::STATUS_NORMAL
            && !connection->outboundQueue.isEmpty()) {
        DispatchEntry* dispatchEntry = connection->outboundQueue.head;
        ...
        EventEntry* eventEntry = dispatchEntry->eventEntry;
        switch (eventEntry->type) {
        case EventEntry::TYPE_KEY: {
            KeyEntry* keyEntry = static_cast<KeyEntry*>(eventEntry);

            // Publish the key event.
            status = connection->inputPublisher.publishKeyEvent(dispatchEntry->seq,
                    keyEntry->deviceId, keyEntry->source,
                    dispatchEntry->resolvedAction, dispatchEntry->resolvedFlags,
                    keyEntry->keyCode, keyEntry->scanCode,
                    keyEntry->metaState, keyEntry->repeatCount, keyEntry->downTime,
                    keyEntry->eventTime);
            break;
        }
        ...
        connection->outboundQueue.dequeue(dispatchEntry);
        connection->waitQueue.enqueueAtTail(dispatchEntry)
    }   
    ...

}

startDispatchCycleLocked方法中調(diào)用publishKeyEvent歹啼,其內(nèi)部會將事件寫入到WMS傳遞下來的InputChannel通道中玄渗。這樣WMS端的InputChannel就可以通過socket獲取到事件信息。在發(fā)送完畢后會將事件移出connection->outboundQueue隊(duì)列狸眼,并放入到waitQueue等待隊(duì)列中藤树,等待事件處理完畢后再移出。

??waitQueue用來監(jiān)聽當(dāng)前分發(fā)給WMS的輸入事件是否已經(jīng)被處理完畢份企。什么時(shí)候知道事件處理完畢呢也榄?

??在InputDispatcher::registerInputChannel方法里面注冊了handleReceiveCallback回調(diào):

status_t InputDispatcher::registerInputChannel(...) {
        ...
        mLooper->addFd(fd, 0, ALOOPER_EVENT_INPUT, handleReceiveCallback, this);
        ...
}

當(dāng)app層的事件處理完畢之后就會回調(diào)handleReceiveCallback:

int InputDispatcher::handleReceiveCallback(int fd, int events, void* data) {
    InputDispatcher* d = static_cast<InputDispatcher*>(data);
    ...
    d->finishDispatchCycleLocked(currentTime, connection, seq, handled);
    ...
    d->runCommandsLockedInterruptible();
    ...
}

這里會先調(diào)用InputDispatcher::finishDispatchCycleLocked去往mCommandQueue里面加入一個(gè)執(zhí)行InputDispatcher:: doDispatchCycleFinishedLockedInterruptible的Command:


void InputDispatcher::doDispatchCycleFinishedLockedInterruptible(
        CommandEntry* commandEntry) {
    sp<Connection> connection = commandEntry->connection;
    ...
    DispatchEntry* dispatchEntry = connection->findWaitQueueEntry(seq);
    ...
    if (dispatchEntry == connection->findWaitQueueEntry(seq)) {
        connection->waitQueue.dequeue(dispatchEntry);
        ...
    }
}

doDispatchCycleFinishedLockedInterruptible中會將connection->waitQueue出棧,這樣整個(gè)輸入系統(tǒng)的分發(fā)過程就閉環(huán)了司志。

3.7 事件分發(fā)流程小結(jié)

  1. ViewRootImpl在setView方法會創(chuàng)建一個(gè)InputChannel通道甜紫,并在將Window添加給WMS的過程時(shí),以參數(shù)傳遞給WMS骂远。

  2. WMS在添加Window的過程中會調(diào)用updateInputWindows囚霸,這個(gè)方法最終會調(diào)用到InputDispatcher::setInputWindows中, 并給InputDispatcher的Window隊(duì)列以及焦點(diǎn)Window賦值激才,這樣IMS就可以找到對應(yīng)的Window了拓型。

  3. 在WMS在添加Window的過程中還會創(chuàng)建一個(gè)socketpair通道的InputChannel额嘿,其中客戶端的socket與app層的InputChannel關(guān)聯(lián),用于WMS與app通訊劣挫;服務(wù)端的socket傳遞給IMS册养,用于IMS和WMS通訊。

  4. 客戶端在接收到輸入事件后压固,會根據(jù)當(dāng)前焦點(diǎn)Window的的InputChannel找到對應(yīng)的Connection連接球拦,這個(gè)Connection用于與WMS進(jìn)行通訊,內(nèi)部其實(shí)就是使用前面的socket通訊帐我。

  5. 事件分發(fā)后將輸入事件放入到waitQueue中坎炼,等待事件處理完畢后,將事件移出waitQueue

3.8 問題復(fù)盤

??那么關(guān)于開頭的兩個(gè)問題:

  • 問題1:窗口什么時(shí)候傳入IMS輸入系統(tǒng)的拦键?

??IMS又是如何找到對應(yīng)的Window窗口的谣光? ViewRootImpl在setView方法中,調(diào)用addToDisplay將Window傳遞給WMS的時(shí)候芬为,這個(gè)時(shí)候會調(diào)用InputMonitor.updateInputWindowsLw方法萄金,最終會調(diào)用到InputDispatcher::setInputWindows,這里面會對IMS的Window屬性進(jìn)行賦值碳柱。IMS根據(jù)前面賦值的Window屬性捡絮,就可以找到對應(yīng)的焦點(diǎn)Window。

  • 問題2:IMS和WMS是通過什么方式通訊將事件分發(fā)出去的莲镣?

??IMS和WMS是通過InputChannel通道進(jìn)行通訊的福稳,WMS在Window添加過程中會創(chuàng)建一個(gè)socket通道,將server端通道傳遞給IMS瑞侮,而client端通道用于WMS中接收server端事件的圆,server端根據(jù)對應(yīng)的Window,找到對應(yīng)的Connection半火,然后使用Connection進(jìn)行通訊越妈,而Connection內(nèi)部就是通過socket進(jìn)行通訊的。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末钮糖,一起剝皮案震驚了整個(gè)濱河市梅掠,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌店归,老刑警劉巖阎抒,帶你破解...
    沈念sama閱讀 206,311評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異消痛,居然都是意外死亡且叁,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評論 2 382
  • 文/潘曉璐 我一進(jìn)店門秩伞,熙熙樓的掌柜王于貴愁眉苦臉地迎上來逞带,“玉大人欺矫,你說我怎么就攤上這事≌姑ィ” “怎么了穆趴?”我有些...
    開封第一講書人閱讀 152,671評論 0 342
  • 文/不壞的土叔 我叫張陵,是天一觀的道長带饱。 經(jīng)常有香客問我毡代,道長阅羹,這世上最難降的妖魔是什么勺疼? 我笑而不...
    開封第一講書人閱讀 55,252評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮捏鱼,結(jié)果婚禮上执庐,老公的妹妹穿的比我還像新娘。我一直安慰自己导梆,他們只是感情好轨淌,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,253評論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著看尼,像睡著了一般递鹉。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上藏斩,一...
    開封第一講書人閱讀 49,031評論 1 285
  • 那天躏结,我揣著相機(jī)與錄音,去河邊找鬼狰域。 笑死媳拴,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的兆览。 我是一名探鬼主播屈溉,決...
    沈念sama閱讀 38,340評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼抬探!你這毒婦竟也來了子巾?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,973評論 0 259
  • 序言:老撾萬榮一對情侶失蹤小压,失蹤者是張志新(化名)和其女友劉穎线梗,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體场航,經(jīng)...
    沈念sama閱讀 43,466評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡缠导,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,937評論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了溉痢。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片僻造。...
    茶點(diǎn)故事閱讀 38,039評論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡憋他,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出髓削,到底是詐尸還是另有隱情竹挡,我是刑警寧澤,帶...
    沈念sama閱讀 33,701評論 4 323
  • 正文 年R本政府宣布立膛,位于F島的核電站揪罕,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏宝泵。R本人自食惡果不足惜好啰,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,254評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望儿奶。 院中可真熱鬧框往,春花似錦、人聲如沸闯捎。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,259評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽瓤鼻。三九已至秉版,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間茬祷,已是汗流浹背清焕。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留牲迫,地道東北人耐朴。 一個(gè)月前我還...
    沈念sama閱讀 45,497評論 2 354
  • 正文 我出身青樓,卻偏偏與公主長得像盹憎,于是被迫代替她去往敵國和親筛峭。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,786評論 2 345

推薦閱讀更多精彩內(nèi)容