Android | InputManagerService與輸入事件采集

前言

  • 事件分發(fā)機制是Android中的基礎(chǔ)而重要的知識,一般認(rèn)為Activity#dispatchKeyEvent()或者Activity#dispatchTouchEvent()是分發(fā)的起點。那么問題來了,是誰調(diào)用了Activity的方法呢?輸入事件是如何產(chǎn)生的?
  • Android系統(tǒng)有一整套從Linux內(nèi)核應(yīng)用框架層應(yīng)用層的事件處理機制
  • 本文將以InputManagerService為線索,分析輸入事件的產(chǎn)生-采集-分發(fā)流程,希望能幫上忙
思維導(dǎo)圖
輸入事件 示意圖

1. 啟動服務(wù)

Android系統(tǒng)啟動后,系統(tǒng)進(jìn)程SystemServer.java將依次啟動各個系統(tǒng)服務(wù),我們搜索下InputManagerService,不會匹配到太多東西,梳理一下有關(guān)的代碼:

關(guān)于系統(tǒng)服務(wù)的更多介紹參考:[Android | 系統(tǒng)啟動過程]

// /frameworks/base/services/java/com/android/server/SystemServer.java
private void startBootstrapServices() {
    InputManagerService inputManager = null;
    WindowManagerService wm = null;
        
    // ...
        
    // 實例化InputManagerService
    inputManager = new InputManagerService(context);
    // 實例化WindowManagerService
    wm = WindowManagerService.main(context,inputManager,...);
    // 添加到ServiceManager統(tǒng)一管理
    ServiceManager.addService(Context.WINDOW_SERVICE, wm, ...);
    ServiceManager.addService(Context.INPUT_SERVICE, inputManager,...);
        
    // ...
        
    inputManager.setWindowManagerCallbacks(wm.getInputMonitor());
    // 啟動InputManager服務(wù)
    inputManager.start();
}
IMS與WMS UML類圖

可以看出,系統(tǒng)進(jìn)程分別實例化了InputManagerServiceWindowManagerService,前者的實例直接傳入后者,而后者又傳遞了一個InputMonitor對象給前者,這是為什么呢?暫時跳過,繼續(xù)往下看InputManagerService.java

// /frameworks/base/services/core/java/com/android/server/input/InputManagerService.java

private static native long nativeInit(InputManagerService service,Context context, MessageQueue messageQueue);

private static native void nativeStart(long ptr);

public InputManagerService(Context context) {
    // ...
    // 調(diào)用了native層
    mPtr = nativeInit(this, mContext, mHandler.getLooper().getQueue());
    // ...
}

public void start() {
    // 調(diào)用了native層
    nativeStart(mPtr);
    // ...
}

這里只是調(diào)用到native層的兩個靜態(tài)方法,繼續(xù)往下看com_android_server_input_InputManagerService.cpp

// /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對象
    NativeInputManager* im = new NativeInputManager(contextObj, serviceObj,messageQueue->getLooper());
    im->incStrong(0);
    return reinterpret_cast<jlong>(im);
}

static void nativeStart(JNIEnv* env, jclass /* clazz */, jlong ptr) {
    NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
    // 調(diào)用到InputManager#start()
    status_t result = im->getInputManager()->start();
}

NativeInputManager::NativeInputManager(jobject contextObj, 
        jobject serviceObj, const sp<Looper>& looper) : 
    mLooper(looper), mInteractive(true) {
    // ...
    // 實例化EventHub對象
    sp<EventHub> eventHub = new EventHub();
    // 實例化InputManager對象
    mInputManager = new InputManager(eventHub, this, this);
}

public: 
inline sp<InputManager> getInputManager() const { return mInputManager; }

可以看出,在native層實例化了NativeInputManager,其構(gòu)造方法里還實例化了InputManagerEventHub對象,后者是干什么的呢?暫時跳過,繼續(xù)往下看InputManager.cpp

// /frameworks/native/services/inputflinger/InputManager.cpp

InputManager::InputManager(
        const sp<EventHubInterface>& eventHub,
        const sp<InputReaderPolicyInterface>& readerPolicy,
        const sp<InputDispatcherPolicyInterface>& dispatcherPolicy) {
    // 實例化InputDispatcher與InputReader
    mDispatcher = new InputDispatcher(dispatcherPolicy);
    // InputDispatcher實例與EventHub實例傳遞給InputReader
    mReader = new InputReader(eventHub, readerPolicy, mDispatcher);
    initialize();
}

void InputManager::initialize() {
    // 實例化兩個線程
    mReaderThread = new InputReaderThread(mReader);
    mDispatcherThread = new InputDispatcherThread(mDispatcher);
}

status_t InputManager::start() {
    // 運行兩個線程
    status_t result = mDispatcherThread->run("InputDispatcher", PRIORITY_URGENT_DISPLAY);
    result = mReaderThread->run("InputReader", PRIORITY_URGENT_DISPLAY);
    // ...
}

可以看出,InputManager的構(gòu)造方法中實例化了InputDispatcherInputReader,隨后start()中啟動了InputDispatcherThread線程與InputReaderThread線程(繼承于Thread.cpp)。

Timethreads圖 - 01

這兩個線程分別做什么事呢?繼續(xù)往下看InputReder.cppInputDispatcher.cpp

// /frameworks/native/services/inputflinger/InputReader.cpp

InputReaderThread::InputReaderThread(const sp<InputReaderInterface>& reader) : 
// 繼承于Thread
Thread(/*canCallJava*/ true), mReader(reader) {
}

/**
 * true:循環(huán)執(zhí)行threadLoop()直到調(diào)用requireExit()退出循環(huán)
 **/
bool InputReaderThread::threadLoop() {
    // 讀取/采集一次事件
    mReader->loopOnce();
    return true;
}
// /frameworks/native/services/inputflinger/InputDispatcher.cpp

InputDispatcherThread::InputDispatcherThread(const sp<InputDispatcherInterface>& dispatcher) : Thread(/*canCallJava*/ true), mDispatcher(dispatcher) {
}

bool InputDispatcherThread::threadLoop() {
    // 分發(fā)一次事件
    mDispatcher->dispatchOnce();
    return true;
}

可以看到,到這里InputManagerService的啟動就完成了,提煉出關(guān)鍵點:

  • InputManagerServiceWindowManagerService運行在系統(tǒng)進(jìn)程SystemServer
  • InputManagerService啟動了InputReaderThread線程與InputDispatcherThread線程,分別死循環(huán)調(diào)用InputReader#loopOnce()InputDispatcher#dispatchOnce()。
Timethreads圖 - 02

2. 采集事件

上一節(jié)講到,InputReaderThread線程死循環(huán)執(zhí)行InputReader#loopOnce(),用于采集事件,簡化代碼如下:

// /frameworks/native/services/inputflinger/InputReader.cpp

void InputReader::loopOnce() {
    // ...
    // 從EventHub中讀取事件,存儲在指針mEventBuffer中
    size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);
    if (count) {
        // 處理讀取到的事件
        processEventsLocked(mEventBuffer, count);
    }
    // ...
    // 這一行是干什么的?稍后介紹
    mQueuedListener->flush();
}

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:輸入事件
            // ...
                        
            // 處理每一個輸入事件
            int32_t deviceId = rawEvent->deviceId;
            processEventsForDeviceLocked(deviceId, rawEvent, batchSize);
        }else{
            // 分支2:設(shè)備事件
            switch(rawEvent->type){
                    case EventHubInterface::DEVICE_ADDED:
                        addDeviceLocked(rawEvent->when, rawEvent->deviceId);
                        break;
                       // ...
            }
        }
    }
    // ...
}

可以看到,loopOnce()EventHub實例中獲取到原始事件,并依次處理每個事件,分為兩種:

  • 輸入事件(分支1)
  • 設(shè)備事件(分支2)

我們先看分支1的processEventsForDeviceLocked(),簡化代碼如下:

// /frameworks/native/services/inputflinger/InputReader.cpp

void InputReader::processEventsForDeviceLocked(int32_t deviceId, const RawEvent* rawEvents, size_t count) {
    ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
    // ...
    InputDevice* device = mDevices.valueAt(deviceIndex);
    // ...
    device->process(rawEvents, count);
}

void InputDevice::process(const RawEvent* rawEvents, size_t count) {
    size_t numMappers = mMappers.size();
    // 遍歷每個事件
    for (const RawEvent* rawEvent = rawEvents; count != 0; rawEvent++) {
            // 依次交給每個InputMapper處理
            for (size_t i = 0; i < numMappers; i++) {
                    InputMapper* mapper = mMappers[i];
                    mapper->process(rawEvent);
            }
    }
}

可以看到,輸入事件交給了InputDevice處理,在內(nèi)部實際上是執(zhí)行了多次InputMapper#process(),這里的InputDeviceInputMapper是什么呢,還記得分支2的addDeviceLocked()嗎?簡化代碼如下:

// /frameworks/native/services/inputflinger/InputReader.cpp

void InputReader::addDeviceLocked(nsecs_t when, int32_t deviceId) {
    // ...
    // 創(chuàng)建InputDevice實例
    InputDevice* device = createDeviceLocked(deviceId, controllerNumber, identifier, classes);
    // ...
    // 添加到mDevices列表 
    // InputReader.h:KeyedVector<int32_t, InputDevice*> mDevices;
    mDevices.add(deviceId, device);
    // ...
}

InputDevice* InputReader::createDeviceLocked(int32_t deviceId, int32_t controllerNumber, const InputDeviceIdentifier& identifier, uint32_t classes) {
    // 創(chuàng)建InputDevice實例
    InputDevice* device = new InputDevice(&mContext, deviceId, bumpGenerationLocked(), controllerNumber, identifier, classes);

    // ...
        
    // Scroll wheel-like devices. - 滾輪式
    if (classes & INPUT_DEVICE_CLASS_ROTARY_ENCODER) {
        device->addMapper(new RotaryEncoderInputMapper(device));
    }
        
    // ...
        
    // Touchscreens and touchpad devices. - 觸屏式
    if (classes & INPUT_DEVICE_CLASS_TOUCH_MT) {
        // 多點觸控設(shè)備
        device->addMapper(new MultiTouchInputMapper(device));
    } else if (classes & INPUT_DEVICE_CLASS_TOUCH) {
        // 單點觸控設(shè)備
        device->addMapper(new SingleTouchInputMapper(device));
    }
        
    // Joystick-like devices. - 操縱桿式
    if (classes & INPUT_DEVICE_CLASS_JOYSTICK) {
        device->addMapper(new JoystickInputMapper(device));
    }
        // ...
}

可以看到分支2里,先創(chuàng)建了InputDevice實例,隨后根據(jù)不同的設(shè)備事件的類型,又添加了不同InputMapper,比如滾輪式,觸屏式,操縱桿式,這也就說明了,一個Android設(shè)備上是支持同時接入多個輸入設(shè)備的,當(dāng)然觸屏設(shè)備才是本文分析的重點。

讓我們回到分支1的InputMapper#process(),因為我們的重點是觸屏設(shè)備,所以我們只關(guān)心MultiTouchInputMapperSingleTouchInputMapper,代碼如下:

// /frameworks/native/services/inputflinger/InputReader.cpp

// 多點觸控設(shè)備
void MultiTouchInputMapper::process(const RawEvent* rawEvent) {
    TouchInputMapper::process(rawEvent);
    // ...
}

// 單點觸控設(shè)備
void SingleTouchInputMapper::process(const RawEvent* rawEvent) {
    TouchInputMapper::process(rawEvent);
    // ...
}

void TouchInputMapper::process(const RawEvent* rawEvent) {
    // ...
    if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) {
        sync(rawEvent->when);
    }
}

void TouchInputMapper::sync(nsecs_t when) {
    // ...
    processRawTouches(false /*timeout*/);
}

void TouchInputMapper::processRawTouches(bool timeout) {
    // ...
    cookAndDispatch(mCurrentRawState.when);
}

void TouchInputMapper::cookAndDispatch(nsecs_t when) {
    // ...
        
    // 按鍵事件
    if (consumeRawTouches(when, policyFlags)) {
        mCurrentRawState.rawPointerData.clear();
    }
    // ...
    // 觸點事件
    dispatchPointerUsage(when, policyFlags, pointerUsage);
    // ...
}

從簡化的代碼可以看出,不論是MultiTouchInputMapper還是SingleTouchInputMapper,最終都會走到cookAndDispatch()這個方法,分別處理了兩類事件:

  • 按鍵事件
  • 觸點事件
// /frameworks/native/services/inputflinger/InputReader.cpp

bool TouchInputMapper::consumeRawTouches(nsecs_t when, uint32_t policyFlags) {
    // ...
    // 處理虛擬按鍵事件
    dispatchVirtualKey(...)
    // ...
}

void TouchInputMapper::dispatchVirtualKey(...) {
    // ...
    // 實例化一個NotifyKeyArgs
    NotifyKeyArgs args(...);
    // 回調(diào)
    getListener()->notifyKey(&args);
}
// /frameworks/native/services/inputflinger/InputReader.cpp

void TouchInputMapper::dispatchPointerUsage(nsecs_t when, uint32_t policyFlags, PointerUsage pointerUsage) {
    // ...
    // 處理觸點事件
    dispatchPointerGestures(when, policyFlags, false /*isTimeout*/);
    // ...
}

void TouchInputMapper::dispatchPointerGestures(nsecs_t when, uint32_t policyFlags, bool isTimeout) {
    // ...
    // 處理觸點事件
    dispatchMotion(...)
    // ...
}

void TouchInputMapper::dispatchMotion(...){
    // ...
    // 實例化一個NotifyMotionArgs
    NotifyMotionArgs args(...);
    // 回調(diào)
    getListener()->notifyMotion(&args);
}

從簡化代碼可以看出,對于按鍵事件和觸點事件,分別實例化了NotifyKeyArgsNotifyMotionArgs,隨后分別調(diào)用了getListener()->notifyKey()getListener()->notifyMotion()。

事件采集 UML時序圖 - 01

getListener()->notifyKey()看起來很像是告知監(jiān)聽器已經(jīng)采集到事件了,是不是這樣呢?找一下getListener()相關(guān)代碼:

// /frameworks/native/services/inputflinger/InputReader.cpp

InputListenerInterface* InputReader::ContextImpl::getListener() {
    // 弱指針的用法,簡單理解為返回了mQueuedListener就好
    return mReader->mQueuedListener.get();
}

// --- InputReader ---

InputReader::InputReader(const sp<EventHubInterface>& eventHub, const sp<InputReaderPolicyInterface>& policy, const sp<InputListenerInterface>& listener) {
    // 實例化QueuedInputListener
    mQueuedListener = new QueuedInputListener(listener);
}

可以看出,getListener()的返回值是mQueuedListener,返回值類型是InputListenerInterface,它在InputReader的構(gòu)造方法中創(chuàng)建,并包裝了InputReader構(gòu)造方法的第三個參數(shù)listener,這個listener又是什么呢?讓我們回去找到實例化InputReader的地方:

// /frameworks/native/services/inputflinger/InputManager.cpp

InputManager::InputManager(...) {
    mDispatcher = new InputDispatcher(dispatcherPolicy);
    // InputDispatcher實例與EventHub實例傳遞給InputReader
    mReader = new InputReader(eventHub, readerPolicy, mDispatcher);
    initialize();
}

原來如此,InputDispatcher實例就是這第三個參數(shù)listener,我們查看InputDispatcherInputListenerInterface的定義:InputDispatcher.hInputListener.h

// /frameworks/native/services/inputflinger/InputDispatcher.h

class InputDispatcher : public InputDispatcherInterface {
    // ...
}

class InputDispatcherInterface : public virtual RefBase, public InputListenerInterface {
    //...
}
// /frameworks/native/services/inputflinger/InputListener.h

/**
 * 基類
 **/
struct NotifyArgs {
    virtual ~NotifyArgs() { }
    virtual void notify(const sp<InputListenerInterface>& listener) const = 0;
};

/**
 * 觸點事件
 **/
struct NotifyMotionArgs : public NotifyArgs {
    // ...
}

/**
 * 按鍵事件
 **/
struct NotifyKeyArgs : public NotifyArgs {
    // ...
}

class InputListenerInterface : public virtual RefBase {/**弱指針**/
    // ...
public:
    virtual void notifyKey(const NotifyKeyArgs* args) = 0;
    virtual void notifyMotion(const NotifyMotionArgs* args) = 0;
        
    // ...  
};

class QueuedInputListener : public InputListenerInterface {
    // ...
public:
    explicit QueuedInputListener(const sp<InputListenerInterface>& innerListener);
        
    virtual void notifyKey(const NotifyKeyArgs* args);
    virtual void notifyMotion(const NotifyMotionArgs* args);
        
    void flush();
        
    // ...

private:
    sp<InputListenerInterface> mInnerListener;
    Vector<NotifyArgs*> mArgsQueue;
};

可以看到,QueuedInputListenerInputDispatcher都是實現(xiàn)了InputListenerInterface,前者其實就是后者的包裝類。

InputDispatcher UML類圖

我們看看包裝類QueuedInputListener都做了什么,簡化代碼如下InputListener.cpp

// /frameworks/native/services/inputflinger/InputListener.cpp

void QueuedInputListener::notifyKey(const NotifyKeyArgs* args) {
    // push到隊列中
    mArgsQueue.push(new NotifyKeyArgs(*args));
}

void QueuedInputListener::notifyMotion(const NotifyMotionArgs* args) {
    // push到隊列中
    mArgsQueue.push(new NotifyMotionArgs(*args));
}

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

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

void QueuedInputListener::flush() {
    size_t count = mArgsQueue.size();
    // 遍歷每個NotifyArgs
    for (size_t i = 0; i < count; i++) {
        NotifyArgs* args = mArgsQueue[i];
        // mInnerListener就是InputDispatcher
        args->notify(mInnerListener);
        delete args;
    }
    mArgsQueue.clear();
}

可以看到,之前的getListener()->notifyMotion()或者getListener()->notifyKey()都是把事件pushmArgsQueue隊列中。那么隊列中的這些事件到底什么時候才被處理呢?還記得InputReader#loopOnce()的最后一步嗎?

// /frameworks/native/services/inputflinger/InputReader.cpp

void InputReader::loopOnce() {
    // ...
    // 從EventHub中讀取事件,存儲在指針mEventBuffer中
    size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);
    if (count) {
        // 處理讀取到的事件
        processEventsLocked(mEventBuffer, count);
    }
    // ...
    mQueuedListener->flush();
}

原來如此,所有事件被pushmArgsQueue隊列之后,會調(diào)用mQueuedListener->flush(),最終會調(diào)用到InputDispatcher#notifyKey()InputDispatcher#notifyMotion(),歸納時序圖如下:

事件采集 UML時序圖 - 02

繼續(xù)查看InputDispatcher#notifyKey()InputDispatcher#notifyMotion()的簡化代碼:

// /frameworks/native/services/inputflinger/InputDispatcher.cpp

void InputDispatcher::notifyKey(const NotifyKeyArgs* args) {
    // ...
        
    // 實例化KeyEntry
    KeyEntry* newEntry = new KeyEntry(...);
    // 加入事件隊列
    enqueueInboundEventLocked(newEntry);
    // 喚醒在mLooper上等待的線程
    mLooper->wake();
}

void InputDispatcher::notifyMotion(const NotifyMotionArgs* args) {
    // ...
        
    // 實例化MotionEntry
    MotionEntry* newEntry = new MotionEntry(...);
    // 加入事件隊列
    enqueueInboundEventLocked(newEntry);
    // 喚醒在mLooper上等待的線程
    mLooper->wake();
}

bool InputDispatcher::enqueueInboundEventLocked(EventEntry* entry) {
    // 加入隊列尾部
    mInboundQueue.enqueueAtTail(entry);
    // ...
}

可以看到,InputDispatcher#notifyKey()InputDispatcher#notifyMotion()基本類似,都是將事件KeyEntryMotionEntry加入隊列mInboundQueue尾部,隨后喚醒在mLooperLooper.h)上等待的線程。

Timethreads圖 - 03

記得我們熟知的生產(chǎn)者-消費者模型嗎?

到這里,我們就完整地在InputReaderThread線程中執(zhí)行了一次loopOnce(),總結(jié)一下關(guān)鍵點:

  • InputReaderEventHub中讀取事件,其中一部分是輸入事件,我們關(guān)心的是按鍵事件觸點事件
  • 事件最終被加入mInboundQueue隊列,隨即喚醒在mLooper上等待的線程

那么,喚醒的是哪個線程呢?會不會就是InputDispatcherThread線程呢?相關(guān)代碼如下:

// /frameworks/native/services/inputflinger/InputDispatcher.cpp

// --- InputDispatcherThread ---

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

InputDispatcher::InputDispatcher(...){
    mLooper = new Looper(false);
}

void InputDispatcher::dispatchOnce() {
    nsecs_t nextWakeupTime = LONG_LONG_MAX;
    // ...
    // 分發(fā)一次事件
    dispatchOnceInnerLocked(&nextWakeupTime);
    // ...
    nsecs_t currentTime = now();
    int timeoutMillis = toMillisecondTimeoutDelay(currentTime, nextWakeupTime);
    // 在mLooper上等待一段時間
    mLooper->pollOnce(timeoutMillis);
}

void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) {
    // ...
    // 獲取mInboundQueue隊列頭部的事件
    mPendingEvent = mInboundQueue.dequeueAtHead();
        
    //...
        
    switch (mPendingEvent->type) {
        // ...
        case EventEntry::TYPE_KEY: {
                // ...
                // 分發(fā)按鍵事件
                done = dispatchKeyLocked(...);
                break;
        }
        case EventEntry::TYPE_MOTION: {
                // ...
                // 分發(fā)觸點事件
                done = dispatchMotionLocked(...);
                break;
        }
    }
    // ...
}

可以看出,InputDispatcherThread線程中死循環(huán)運行dispatchOnce(),每一次都從mInboundQueue隊列中獲取頭部的事件,并執(zhí)行一次事件分發(fā)。每分發(fā)一次事件后都會調(diào)用mLooper->pollOnce()等待一段時間。等待的過程中,可能被InputReaderThread線程中執(zhí)行的mLooper.wake()提前喚醒,從而觸發(fā)下一次事件分發(fā)。


到這里,我們就完整分析了InputManagerService的事件采集流程,下一篇文章我們將討論事件分發(fā)過程,歡迎關(guān)注彭旭銳的簡書!


延伸閱讀

  • [Android | 系統(tǒng)啟動過程]
  • [Android | InputManagerService與輸入事件分發(fā)]

推薦閱讀


感謝喜歡!你的點贊是對我最大的鼓勵!歡迎關(guān)注彭旭銳的簡書!

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

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

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