Android10_原理機制系列_事件傳遞機制

流浪_歸家發表於2020-12-08

前言和概述

Android的輸入裝置,最常用的就是 觸控式螢幕和按鍵 了。當然還有其他方式,比如遊戲手柄,比如支援OTG裝置,則可以連結滑鼠、鍵盤等。
那麼這些裝置的操作 是如何傳遞到系統 並 控制介面的呢?系統如何知道是如何知道點選了某個介面按鈕,按了某個按鍵,知道交給哪個應用處理的?
該篇主要介紹這些,即 輸入事件從生成(存於裝置節點中) 傳遞到目標View的過程。 在進入輸入事件傳遞機制之前,首先了解一個東西---裝置節點。

瞭解裝置節點

當有輸入事件時,Linux核心會將事件資料寫入 裝置節點 中,供上層讀取最終傳遞到具體的View中。 該備節點 位於/dev/input/。

1. 檢視:輸入事件相關裝置資訊

與事件相關的裝置資訊位於:/proc/bus/input/devices。
下面是一部分,大致瞭解下。Name對應的Handlers注意下。

//檢視所有事件相關裝置資訊
$ adb shell cat /proc/bus/input/devices
    I: Bus=0019 Vendor=0000 Product=0000 Version=0000
    N: Name="ACCDET"
    P: Phys=
    S: Sysfs=/devices/virtual/input/input0
    U: Uniq=
    H: Handlers=event0
    B: PROP=0
    B: EV=23
    B: KEY=40 0 0 0 0 0 0 0 0 0 0 0 0 10 0 c0000 0 0 0
    B: SW=d4

    I: Bus=0019 Vendor=2454 Product=6500 Version=0010
    N: Name="mtk-kpd"
    P: Phys=
    S: Sysfs=/devices/platform/10010000.kp/input/input1
    U: Uniq=
    H: Handlers=event1
    B: PROP=0
    B: EV=3
    B: KEY=1000000 0 0 0 0 0 0 0 0 1c0000 0 0 0
    ...

2. 命令:getevent 和 sendenvent

2.1 getevent

通過裝置的getevent命令,可以檢視輸入事件的資訊。

//獲取輸入事件,這裡是按了下電源鍵
$ adb shell getevent
    add device 1: /dev/input/event0
      name:     "ACCDET"
    add device 2: /dev/input/event2
      name:     "sf-keys"
    add device 3: /dev/input/event3
      name:     "mtk-tpd"
    add device 4: /dev/input/event1
      name:     "mtk-kpd"
    /dev/input/event1: 0001 0074 00000001
    /dev/input/event1: 0000 0000 00000000
    /dev/input/event1: 0001 0074 00000000
    /dev/input/event1: 0000 0000 00000000

//從/proc/bus/input/devices獲取到要關注的裝置的節點,可以單獨獲取
//下面是獲取也是按的電源鍵獲取到的
$ adb shell getevent  /dev/input/event1
    0001 0074 00000001
    0000 0000 00000000
    0001 0074 00000000
    0000 0000 00000000

//多瞭解引數,這個-l就很清楚了
//-l: label event types and names in plain text
$ adb shell getevent -l /dev/input/event1
    EV_KEY       KEY_POWER            DOWN
    EV_SYN       SYN_REPORT           00000000
    EV_KEY       KEY_POWER            UP
    EV_SYN       SYN_REPORT           00000000

上面列出的3種,第一種沒有引數 獲取所有輸入事件。加上 -l引數 的結果就很清晰了。
事件型別: 0001 即 EV_KEY,按鍵
事件程式碼: 0074 即 KEY_POWER,電源鍵
事件的值: 00000001 即 DOWN,按下;00000000 即 UP,抬起。

/dev/input/event1: 0001 0074 00000001 就是 電源鍵按下了。
/dev/input/event1: 0001 0074 00000000 就是 電源鍵抬起了。

注意:這裡的值 都是 16進位制的。

觸控螢幕也一樣:

//觸控螢幕擷取
/dev/input/event3: EV_ABS       ABS_MT_TOUCH_MAJOR   0000001e
/dev/input/event3: EV_ABS       ABS_MT_TRACKING_ID   00000000
/dev/input/event3: EV_ABS       ABS_MT_POSITION_X    000001b5
/dev/input/event3: EV_ABS       ABS_MT_POSITION_Y    000001e1
/dev/input/event3: EV_SYN       SYN_MT_REPORT        00000000
/dev/input/event3: EV_SYN       SYN_REPORT           00000000

2.2 sendenvent

輸入事件 裝置節點也是可寫的,通過sendevent可模擬使用者輸入。
sendevent 的引數是 十進位制。

格式:sendevent <裝置節點> <事件型別> <事件程式碼> <事件的值>

所以getevent中,電源按下/抬起的:事件型別即1,事件程式碼即116,事件的值即1/0。

//電源鍵按下
$ adb shell sendevent /dev/input/event1 1 116 1
//電源鍵抬起
$ adb shell sendevent /dev/input/event1 1 116 0
//由上述getevent個人理解,0 0 0上報後生效,同按一次電源鍵操作
$ adb shell sendevent /dev/input/event1 0 0 0

概述

該篇也是基於Android10的程式碼分析。 該篇寫時後期調整過幾次標題編號,如果文中有參考的編號不對應,請指出。下面圖片由於部落格顯示不是原圖,可能部分不清晰,可以單獨檢視圖片原圖。
文章很長,但分的3個模組比較清晰,可以根據需要檢視。
好,這裡正式開始了。


下面是畫的一張圖,即本章的大致內容。也是方便自己查閱,主要介紹了 輸入事件是如何從 裝置節點中 傳遞到具體的View的。

整篇文章比較長,需要耐心。事件傳遞的過程 與 WMS關係比較密切,若有需要可以先參考:Android10_原理機制系列_Activity視窗新增到WMS過程Android10_原理機制系列_Window介紹及WMS的啟動過程。

若有不對,歡迎指出:

wms_ims

說明:

  • 圖中3個紅色虛線框: 即下面輸入事件傳遞 介紹的3部分內容。IMS中事件的讀取和派發;WMS中Window獲取事件和傳遞;View中事件的傳遞和處理。
  • 圖中2種顏色區域: 標識2個程式。system_server 和 應用程式。
  • 圖中紅色實線箭頭: 文章介紹的 事件傳遞的 主要過程。
  • 圖中2個紅色虛線箭頭: 列出了 兩個比較常見的policy攔截的大致階段 (當然不止這兩個),說明了最終如何回撥到PhoneWindowManager的同名方法。

輸入事件的傳遞

輸入事件的傳遞過程,如概述中所述,這裡分成了3個部分來說明。

  • IMS中事件的讀取和派發
  • WMS中Window獲取事件和傳遞
  • View中事件的傳遞和處理

下面來具體看看。

1. IMS中事件的讀取和派發

我們從IMS(InputManagerService)的建立和啟動開始看。

IMS是在SystemServer的 startOtherServices() 方法中啟動。(之前總結了AMS/PMS/WMS等,這裡類似)

//SystemServer.java
private void startOtherServices() {
    WindowManagerService wm = null;
    InputManagerService inputManager = null;
    try {
        //參考1.1,建立IMS物件
        inputManager = new InputManagerService(context);
        wm = WindowManagerService.main(context, inputManager, !mFirstBoot, mOnlyCore,
                new PhoneWindowManager(), mActivityManagerService.mActivityTaskManager);
        //註冊服務:"input"
        ServiceManager.addService(Context.INPUT_SERVICE, inputManager,
                /* allowIsolated= */ false, DUMP_FLAG_PRIORITY_CRITICAL);
        //參考1.4,設定回撥
        inputManager.setWindowManagerCallbacks(wm.getInputManagerCallback());
        //參考1.5, 啟動
        inputManager.start();
    }
    final InputManagerService inputManagerF = inputManager;
    mActivityManagerService.systemReady(() -> {
        try {
            if (inputManagerF != null) {
                inputManagerF.systemRunning();
            }
        }
    }, BOOT_TIMINGS_TRACE_LOG);
}

SystemServer中 關於IMS主要看3個內容:

  • new InputManagerService(),最後進入native 最終建立了InputManager及一系列相關內容。
  • inputManager.setWindowManagerCallbacks(),設定了回撥,這裡說明了 最終如何回撥到PhoneWindowManager。
  • inputManager.start(),主要是IntputManager中兩個執行緒執行起來。InputReaderThread讀取和處理,InputDispatcherThread派發。

1.1 建立了InputManager

先看IMS的構造方法:

//InputManagerService.java
public class InputManagerService extends IInputManager.Stub implements Watchdog.Monitor {
    private static native long nativeInit(InputManagerService service,
        Context context, MessageQueue messageQueue);
    
    public InputManagerService(Context context) {
        this.mContext = context;
        //建立了InputManagerHandler,其Looper是DisplayThead的Looper
        this.mHandler = new InputManagerHandler(DisplayThread.get().getLooper());
        //進入native,並返回了mPtr
        mPtr = nativeInit(this, mContext, mHandler.getLooper().getQueue());
        LocalServices.addService(InputManagerInternal.class, new LocalService());
    }
    
    private final class InputManagerHandler extends Handler {
        public InputManagerHandler(Looper looper) {
            super(looper, null, true /*async*/);
        }
    }
}

看到 this.mHandler 的Looper 是 DisplayThread的Looper。 這個Looper的訊息佇列 作為引數 傳入到 nativeInit() 方法中。關於Looper,如果不太瞭解,也可以參考:Android10_原理機制系列_Android訊息機制(Handler)詳述

下面進入 nativeInit() 。

//com_android_server_input_InputManagerService.cpp
static const JNINativeMethod gInputManagerMethods[] = {
    /* name, signature, funcPtr */
    { "nativeInit",
     "(Lcom/android/server/input/InputManagerService;Landroid/content/Context;Landroid/os/MessageQueue;)J",
      (void*) nativeInit },
}

static jlong nativeInit(JNIEnv* env, jclass /* clazz */,
        jobject serviceObj, jobject contextObj, jobject messageQueueObj) {
    sp<MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj);
    ...
    //建立NativeInputManager
    NativeInputManager* im = new NativeInputManager(contextObj, serviceObj,
            messageQueue->getLooper());
    //system/core/libutils/RefBase.cpp檢視
    im->incStrong(0);
    //返回給IMS,IMS後續會用到。IMS儲存在mPtr。
    return reinterpret_cast<jlong>(im);
}

//com_android_server_input_InputManagerService.cpp
//NativeInputManager的構造方法:
NativeInputManager::NativeInputManager(jobject contextObj,
        jobject serviceObj, const sp<Looper>& looper) :
        mLooper(looper), mInteractive(true) {
    //建立InputManager
    mInputManager = new InputManager(this, this);
    defaultServiceManager()->addService(String16("inputflinger"),
            mInputManager, false);
}

這裡看到,nativeInit() 中建立NativeInputManager。 返回給IMS的是 reinterpret_cast<jlong>(im) ,這是某種轉換,可以看作是將NativeInputManager返回給了java層。

NativeInputManager中又建立了 InputManager。接著看InputManager的建立:

//InputManager.cpp
InputManager::InputManager(
        const sp<InputReaderPolicyInterface>& readerPolicy,
        const sp<InputDispatcherPolicyInterface>& dispatcherPolicy) {
    //建立InputDispatcher
    mDispatcher = new InputDispatcher(dispatcherPolicy);
    mClassifier = new InputClassifier(mDispatcher);
    //建立InputReader
    mReader = createInputReader(readerPolicy, mClassifier);
    //建立了兩個執行緒 InputReaderThread和InputDispatcherThread
    initialize();
}

void InputManager::initialize() {
    mReaderThread = new InputReaderThread(mReader);
    mDispatcherThread = new InputDispatcherThread(mDispatcher);
}

//InputReaderFactory.cpp
sp<InputReaderInterface> createInputReader(
        const sp<InputReaderPolicyInterface>& policy,
        const sp<InputListenerInterface>& listener) {
    //EventHub作為引數 傳入InputReader
    return new InputReader(new EventHub(), policy, listener);
}

上述程式碼,可以看到,InputManager中基本都是建立操作,建立了InputDispatcher、InputClassifier、InputReader、EventHub、InputReaderThread、InputDispatcherThread。

下面會逐步看到他們的作用 以及如何執行的。 這裡先簡單說明下其中幾個主要的部分, 先有個大致瞭解。

  • EventHub:建立InputReader時 可以看到先建立了EventHub作為引數。

    EventHub 通過Linux核心的INotify與Epoll機制 監聽裝置,可直接訪問 裝置節點。通過 getEvents() 方法 讀取裝置節點的原始輸入事件 資料。

    關於 EventHub的建立 這裡不討論了,這裡只需簡單瞭解它上面一點就可以了。它涉及核心和一些機制,暫時我也還不熟悉,哈哈。

  • InputReader:負責輸入事件的獲取。在獨立執行緒(InputReaderThread)中 迴圈執行,有以下幾個功能:

    功能1-通過 EventHub 不斷 獲取裝置節點的 原始輸入資料

    功能2-然後 進行加工處理後 交由 InputDispatcher分派

    功能3-它還有 管理 輸入裝置列表和配置

  • InputDispatcher:負責輸入事件的派發。在獨立執行緒(InputDispatcherThread)中執行,其儲存有WMS的所有視窗資訊。

    在接收到 InputReader 的輸入事件後,會在視窗資訊中找到合適的 視窗 並 派發訊息。

  • InputReaderThread、InputDispatcherThread:因為InputReader 和 InputDispatcher都是耗時操作,因此建立 單獨執行緒 來執行他們。這就是他們執行的執行緒。

建立完成後,他們是如何聯絡 並 執行的?

這個下面從 InputReaderThread和InputDispatcherThread兩個執行緒的執行起來 理一下就可以大致瞭解。

1.2 InputReaderThread的執行:InputReader讀取和處理事件

這裡從InputReaderThread的執行開始介紹。

關於InputReaderThread和InputDispatcherThread 是如何執行起來,如何執行的threadLoop() ,後面也介紹了,請參考1.5

1.2.1 InputReader 獲取輸入事件

這裡從 InputReaderThread::threadLoop() 開始跟蹤:

//InputReaderBase.cpp
bool InputReaderThread::threadLoop() {
    mReader->loopOnce();
    return true;
}

//InputReader.cpp
void InputReader::loopOnce() {
    ...
    //獲取事件
    size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);
    { // acquire lock
        AutoMutex _l(mLock);
        mReaderIsAliveCondition.broadcast();
        if (count) {
            //處理輸入事件,參考1.2.2
            processEventsLocked(mEventBuffer, count);
        }
        ...
    } // release lock
    // Send out a message that the describes the changed input devices.
    if (inputDevicesChanged) {
        mPolicy->notifyInputDevicesChanged(inputDevices);
    }
    // Flush queued events out to the listener.
    // This must happen outside of the lock because the listener could potentially call
    // back into the InputReader's methods, such as getScanCodeState, or become blocked
    // on another thread similarly waiting to acquire the InputReader lock thereby
    // resulting in a deadlock.  This situation is actually quite plausible because the
    // listener is actually the input dispatcher, which calls into the window manager,
    // which occasionally calls into the input reader.
    //將事件 推送給 InputDispatcher 進行處理。參考1.2.2.3
    mQueuedListener->flush();
    ...
}

執行緒 執行起來後,會執行threadLoop,這裡返回true,會迴圈執行該threadLoop方法。

threadLoop中呼叫 loopOnce,通過3步將訊息 傳送到InputDispatcher:

  • 通過 mEventHub->getEvents()獲取所有 輸入事件的原始資料。 這部分該篇不討論
  • 通過 processEventsLocked() 處理輸入事件。 參考1.2.2
  • 通過 mQueuedListener->flush() 推送到InputDispatcher。 參考1.2.2.3

1.2.2 InputReader 處理輸入事件

看下處理輸入事件的方法:processEventsLocked()

//InputReader.cpp
void InputReader::processEventsLocked(const RawEvent* rawEvents, size_t count) {
    for (const RawEvent* rawEvent = rawEvents; count;) {
        int32_t type = rawEvent->type;
        size_t batchSize = 1;
        if (type < EventHubInterface::FIRST_SYNTHETIC_EVENT) {
            int32_t deviceId = rawEvent->deviceId;
            while (batchSize < count) {
                if (rawEvent[batchSize].type >= EventHubInterface::FIRST_SYNTHETIC_EVENT
                        || rawEvent[batchSize].deviceId != deviceId) {
                    break;
                }
                batchSize += 1;
            }
            //處理 真正的輸入事件,參考 1.2.2.2
            processEventsForDeviceLocked(deviceId, rawEvent, batchSize);
        } else {
            //處理 裝置增加、刪除、掃描更新。參考1.2.2.1
            switch (rawEvent->type) {
            case EventHubInterface::DEVICE_ADDED:
                addDeviceLocked(rawEvent->when, rawEvent->deviceId);
                break;
            ...
            }
        }
        count -= batchSize;
        rawEvent += batchSize;
    }
}

前面講了InputReader有3個功能,這裡可以看到功能2和3:對輸入事件進行加工處理後 交由InputDispatcher;對輸入裝置 列表的管理和配置。先看功能3,在看功能2。

1.2.2.1 瞭解輸入裝置的管理

先看下功能3:對輸入裝置 列表的管理和配置。

這裡以增加裝置 為例,看下addDeviceLocked():

//InputReader.cpp
void InputReader::addDeviceLocked(nsecs_t when, int32_t deviceId) {
    ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
    if (deviceIndex >= 0) {
        ALOGW("Ignoring spurious device added event for deviceId %d.", deviceId);
        return;
    }
    InputDeviceIdentifier identifier = mEventHub->getDeviceIdentifier(deviceId);
    uint32_t classes = mEventHub->getDeviceClasses(deviceId);
    int32_t controllerNumber = mEventHub->getDeviceControllerNumber(deviceId);
    //建立InputDevice
    InputDevice* device = createDeviceLocked(deviceId, controllerNumber, identifier, classes);
    device->configure(when, &mConfig, 0);
    device->reset(when);
    ...
	//加入mDevices
    mDevices.add(deviceId, device);
    ...
}

//mDevices 和 InputDevice定義(擷取部分)
//InputReader.h
class InputReader : public InputReaderInterface {
	KeyedVector<int32_t, InputDevice*> mDevices;
}
class InputDevice {
	int32_t mId;//通過mId從EventHub中找到對應的輸入裝置
	std::vector<InputMapper*> mMappers;//處理上報的事件
}

這裡建立了一個InputDevice,然後將其加入到mDevices。mDevices 中儲存了 裝置的id 以及 對應的InputDevice。

EventHub 中也有個 mDevices,儲存了 裝置的id 和 對應的Device資訊。 如下(擷取部分):

//EventHub.h
class EventHub : public EventHubInterface {
    KeyedVector<int32_t, Device*> mDevices;
    
    struct Device {
        Device* next;
        int fd; // may be -1 if device is closed  //裝置節點 的檔案控制程式碼
        const int32_t id;
        const std::string path;
        const InputDeviceIdentifier identifier; //記錄裝置資訊,裝置的名稱、供應商、型號等等
        std::unique_ptr<TouchVideoDevice> videoDevice;
        uint32_t classes;
		std::unique_ptr<VirtualKeyMap> virtualKeyMap;
        KeyMap keyMap;
	}
}

看下 建立InputDevice的過程,createDeviceLocked():

//InputReader.cpp
InputDevice* InputReader::createDeviceLocked(int32_t deviceId, int32_t controllerNumber,
        const InputDeviceIdentifier& identifier, uint32_t classes) {
    //建立InputDevice
    InputDevice* device = new InputDevice(&mContext, deviceId, bumpGenerationLocked(),
            controllerNumber, identifier, classes);

    // External devices.
    if (classes & INPUT_DEVICE_CLASS_EXTERNAL) {
        device->setExternal(true);
    }
    // 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;
}

void InputDevice::addMapper(InputMapper* mapper) {
    mMappers.push_back(mapper);
}

建立了InputDevice後,進行一些設定。值得關注的是 一個InputDevice儲存了多個 InputMapper,這些InputMapper儲存在mMappers。

簡單理一下:InputReader新增裝置,首先建立了一個InputDevice,然後加入到mDevices中。而根據裝置型別,可以建立多個InputMapper,這多個InputMapper儲存在InputDevice中的mMappers中。

1.2.2.2 輸入事件處理

接著看功能2,對輸入事件進行處理 processEventsForDeviceLocked() :

//InputReader.cpp
void InputReader::processEventsForDeviceLocked(int32_t deviceId,
        const RawEvent* rawEvents, size_t count) {
    ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
    //最終根據deviceId獲得裝置對應的InputDevice
    InputDevice* device = mDevices.valueAt(deviceIndex);
    //事件交由 對應InputDevice處理。rawEvents是一組事件,可以注意下來源。                       
    device->process(rawEvents, count);
}

//InputReader.cpp
void InputDevice::process(const RawEvent* rawEvents, size_t count) {
    // Process all of the events in order for each mapper.
    // We cannot simply ask each mapper to process them in bulk because mappers may
    // have side-effects that must be interleaved.  For example, joystick movement events and
    // gamepad button presses are handled by different mappers but they should be dispatched
    // in the order received.
    for (const RawEvent* rawEvent = rawEvents; count != 0; rawEvent++) {
        ...
        if (mDropUntilNextSync) {
            if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) {
                mDropUntilNextSync = false;
                ...
            } 
            ...
        } else if (rawEvent->type == EV_SYN && rawEvent->code == SYN_DROPPED) {
            ALOGI("Detected input event buffer overrun for device %s.", getName().c_str());
            mDropUntilNextSync = true;
            reset(rawEvent->when);
        } else {
			//將InputDvices物件中的mMappers依次取出來,呼叫process()進行處理
            for (InputMapper* mapper : mMappers) {
                mapper->process(rawEvent);
            }
        }
        --count;
    }
}

InputReader 獲得某裝置相關一組事件,然後找到對應InputDevice進行處理,執行 InputDevice::process()

InputDevice則將InputDvices物件中的mMappers依次取出來,呼叫process()進行處理。各個 InputMapper 對事件進行判斷,若是屬於自己處理的型別 再進行不同的處理。

下面 以鍵盤事件 為例說明,則InputMapper是KeyboardInputMapper:

//InputReader.cpp
void KeyboardInputMapper::process(const RawEvent* rawEvent) {
    switch (rawEvent->type) {
        case EV_KEY: {
            int32_t scanCode = rawEvent->code;
            int32_t usageCode = mCurrentHidUsage;
            mCurrentHidUsage = 0;
            if (isKeyboardOrGamepadKey(scanCode)) {
                //處理事件,這裡即處理按鍵的方法
                processKey(rawEvent->when, rawEvent->value != 0, scanCode, usageCode);
            }
            break;
        }
        ...
    }
}

//InputReader.cpp
//核心上報的掃描碼(scanCode),轉換成Android系統使用的按鍵碼(keyCode),重構NotifyArgs,加入 mArgsQueue佇列
void KeyboardInputMapper::processKey(nsecs_t when, bool down, int32_t scanCode,
        int32_t usageCode) {
    int32_t keyCode;
    int32_t keyMetaState;
    uint32_t policyFlags;
    ...
    //重構args
    NotifyKeyArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), mSource,
            getDisplayId(), policyFlags, down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP,
            AKEY_EVENT_FLAG_FROM_SYSTEM, keyCode, scanCode, keyMetaState, downTime);
    //插入到 mArgsQueue 佇列中
    getListener()->notifyKey(&args);
}

這個處理過程 主要是 封裝各個引數,重新構造成 NotifyKeyArgs ,然後 將構造的 NotifyKeyArgs物件加入 mArgsQueue佇列。

加入到 mArgsQueue的過程, getListener()->notifyKey(&args):

//InputReader.cpp
InputListenerInterface* InputReader::ContextImpl::getListener() {
    return mReader->mQueuedListener.get();
}
InputReader::InputReader(const sp<EventHubInterface>& eventHub,
        const sp<InputReaderPolicyInterface>& policy,
        const sp<InputListenerInterface>& listener) : ... {
    mQueuedListener = new QueuedInputListener(listener);
}

//InputListener.cpp
QueuedInputListener::QueuedInputListener(const sp<InputListenerInterface>& innerListener) :
        mInnerListener(innerListener) {
}
void QueuedInputListener::notifyKey(const NotifyKeyArgs* args) {
    mArgsQueue.push_back(new NotifyKeyArgs(*args));//push_back() 在Vector尾部插入
}
//InputListener.h
class QueuedInputListener : public InputListenerInterface {
    std::vector<NotifyArgs*> mArgsQueue;
};

在1.1中 建立InputReader時已經知道(可以回去看下),InputReader中的lister是InputClassifier物件,所以 QueuedInputListener中的innerListener 也就是 InputClassifier。

到這裡再理一下:事件先交由了對應的InputDevice,然後找對處理該事件型別的InputMapper 進行處理。InputMapper 將事件等資訊 構造了NotifyArgs,然後加入到了mArgsQueue中。

1.2.2.3 輸入事件傳入到 InputDispatcher

看 InputReader::loopOnce() 的最後一句:mQueuedListener->flush();

//InputListener.cpp
void QueuedInputListener::flush() {
    size_t count = mArgsQueue.size();
    //依次從mArgsQueue中取出NotifyArgs
    for (size_t i = 0; i < count; i++) {
        NotifyArgs* args = mArgsQueue[i];
        //mInnerListener是InputClassifier,上面(1.2.2.2最後)已經特意指出
        args->notify(mInnerListener);
        delete args;
    }
    mArgsQueue.clear();
}

如註釋所說。接著看 args->notif(),接下來都是以鍵盤事件為例:

//NotifyArgs是所有args的超類。 以鍵盤為例,args即NotifyKeyArgs
void NotifyKeyArgs::notify(const sp<InputListenerInterface>& listener) const {
    listener->notifyKey(this);
}


//InputManager.cpp
InputManager::InputManager(
        const sp<InputReaderPolicyInterface>& readerPolicy,
        const sp<InputDispatcherPolicyInterface>& dispatcherPolicy) {
    mDispatcher = new InputDispatcher(dispatcherPolicy);
    mClassifier = new InputClassifier(mDispatcher);
}
//InputClassifier.cpp
//mListener是InputDispatcher
InputClassifier::InputClassifier(const sp<InputListenerInterface>& listener)
      : mListener(listener), mHalDeathRecipient(new HalDeathRecipient(*this)) {}


void InputClassifier::notifyKey(const NotifyKeyArgs* args) {
    // pass through
    mListener->notifyKey(args);
}

很清楚列出了,這裡的mListener即InputDispatcher。所以最終走到了 InputDispatcher::notifyKey():

//InputDispatcher.cpp
void InputDispatcher::notifyKey(const NotifyKeyArgs* args) {
    ...
    int32_t keyCode = args->keyCode;
    //Meta + Backspace -> generate BACK; Meta + Enter -> generate HOME
    accelerateMetaShortcuts(args->deviceId, args->action, keyCode, metaState);

    KeyEvent event;
    event.initialize(args->deviceId, args->source, args->displayId, args->action,
            flags, keyCode, args->scanCode, metaState, repeatCount,
            args->downTime, args->eventTime);

    android::base::Timer t;
    //mPolicy是NativeInputManager,最終回撥到PhoneWindowManager的同名方法。參考1.2.2.4
    mPolicy->interceptKeyBeforeQueueing(&event, /*byref*/ policyFlags);
    ...
    bool needWake;
    { // acquire lock
        mLock.lock();
        ...
        KeyEntry* newEntry = new KeyEntry(args->sequenceNum, args->eventTime,
                args->deviceId, args->source, args->displayId, policyFlags,
                args->action, flags, keyCode, args->scanCode,
                metaState, repeatCount, args->downTime);

        needWake = enqueueInboundEventLocked(newEntry);
        mLock.unlock();
    } // release lock

    if (needWake) {
        mLooper->wake();
    }
}

bool InputDispatcher::enqueueInboundEventLocked(EventEntry* entry) {
    bool needWake = mInboundQueue.isEmpty();
    //entry加入到mInboundQueue
    mInboundQueue.enqueueAtTail(entry);
    ...
    return needWake;
}

最終,輸入事件 由InputReader 獲取處理後,傳遞到InputDispatcher,封裝成EventEntry並加入mInboundQueue 佇列了。

1.2.2.4 事件入隊前的攔截:interceptKeyBeforeQueueing()

注意上面 InputDispatcher::notifyKey 中有 mPolicy->interceptKeyBeforeQueueing(&event, /*byref*/ policyFlags); 這句話,對PhoneWindowManager有過了解的,應該比較清楚。

這裡就是 policy攔截的 比較常見的一處,從這最終回撥 的是 PhoneWindowManager中的方法。

這個大致看下,這裡mPolicy即 NativeInputManager ,所以直接看NativeInputManager::interceptKeyBeforeQueueing()

//com_android_server_input_InputManagerService.cpp
void NativeInputManager::interceptKeyBeforeQueueing(const KeyEvent* keyEvent,
        uint32_t& policyFlags) {
    ...
	wmActions = env->CallIntMethod(mServiceObj,
                    gServiceClassInfo.interceptKeyBeforeQueueing,
                    keyEventObj, policyFlags);
    ...
}
//InputManagerService.java
private int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags) {
    return mWindowManagerCallbacks.interceptKeyBeforeQueueing(event, policyFlags);
}

這個跟蹤 就是執行了IMS中的interceptKeyBeforeQueueing()方法。

最終是如何呼叫到 PhoneWindowManager 中的方法的?

這裡的mWindowManagerCallbacks是 wms中建立的InputManagerCallback物件。這個如何來的 參考1.4。

所以 mWindowManagerCallbacks.interceptKeyBeforeQueueing(event, policyFlags); 即:

//InputManagerCallback.java
public InputManagerCallback(WindowManagerService service) {
    mService = service;
}
@Override
public int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags) {
    return mService.mPolicy.interceptKeyBeforeQueueing(event, policyFlags);
}

這裡的mService.mPolicy就是PhoneWindowManager物件,在WMS建立時設定的。所以最終 回撥的 PhoneWindowManager 中的 interceptKeyBeforeQueueing() 方法。 PhoneWindowManager 是 WindowManagerPolicy 的實現類。

1.3 InputDispatcherThread執行:InputDispatcher派發事件

前面講到,輸入事件 在InputDispatcher中 封裝成EventEntry並加入mInboundQueue 佇列了。接著看 InputDispatcher是如何繼續處理 派發的。
如同InputReaderThread中介紹,這裡直接看threadLoop()。

//frameworks/native/services/inputflinger/InputDispatcher.cpp
bool InputDispatcherThread::threadLoop() {
    mDispatcher->dispatchOnce();
    return true;
}

void InputDispatcher::dispatchOnce() {
    nsecs_t nextWakeupTime = LONG_LONG_MAX;
    { // acquire lock
        std::scoped_lock _l(mLock);
        mDispatcherIsAlive.notify_all();

        // Run a dispatch loop if there are no pending commands.
        // The dispatch loop might enqueue commands to run afterwards.
        //若mCommandQueue為空
        if (!haveCommandsLocked()) {
            //參考1.3.1
            dispatchOnceInnerLocked(&nextWakeupTime);
        }

        // Run all pending commands if there are any.
        // If any commands were run then force the next poll to wake up immediately.
        //參考1.3.4,執行 mCommandQueue 中命令
        if (runCommandsLockedInterruptible()) {
            nextWakeupTime = LONG_LONG_MIN;
        }
    } // release lock

    // Wait for callback or timeout or wake.  (make sure we round up, not down)
    nsecs_t currentTime = now();
    int timeoutMillis = toMillisecondTimeoutDelay(currentTime, nextWakeupTime);
    mLooper->pollOnce(timeoutMillis);
}

//InputDispatcher.h
EventEntry* mPendingEvent GUARDED_BY(mLock);
Queue<EventEntry> mInboundQueue GUARDED_BY(mLock);
Queue<EventEntry> mRecentQueue GUARDED_BY(mLock);
Queue<CommandEntry> mCommandQueue GUARDED_BY(mLock);

//InputDispatcher.cpp
bool InputDispatcher::haveCommandsLocked() const {
    return !mCommandQueue.isEmpty();
}

1.3.1 InputDispatcher 取得輸入事件

1.2.2.3講到:輸入事件 由InputReader 獲取處理後,加入到了InputDispatcher中的 mInboundQueue 佇列了。

事件派發 首先從 mInboundQueue佇列中 取出輸入事件,然後進行處理。

//InputDispatcher.cpp
void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) {
    ...
    // Ready to start a new event.
    // If we don't already have a pending event, go grab one.
    if (! mPendingEvent) {
        if (mInboundQueue.isEmpty()) {
            ...
        } else {
            // Inbound queue has at least one entry.
            //從mInboundQueue中 出隊 一個元素
            mPendingEvent = mInboundQueue.dequeueAtHead();
            traceInboundQueueLengthLocked();
        }

        // Poke user activity for this event.
        if (mPendingEvent->policyFlags & POLICY_FLAG_PASS_TO_USER) {
            pokeUserActivityLocked(mPendingEvent);
        }

        // Get ready to dispatch the event.
        //這裡注意下,ANR相關。這裡先mark下,這篇不說明
        resetANRTimeoutsLocked();
    }

    // Now we have an event to dispatch.
    // All events are eventually dequeued and processed this way, even if we intend to drop them.
    ...

    switch (mPendingEvent->type) {
    ...
    case EventEntry::TYPE_KEY: {
        KeyEntry* typedEntry = static_cast<KeyEntry*>(mPendingEvent);
        ...
        //分派事件
        done = dispatchKeyLocked(currentTime, typedEntry, &dropReason, nextWakeupTime);
        break;
    }
    ...
}

取出事件後,然後分派,這裡同樣以 鍵盤按鍵事件為例,直接看 dispatchKeyLocked():

1.3.2 分派事件前處理

直接看 dispatchKeyLocked():

//InputDispatcher.cpp
bool InputDispatcher::dispatchKeyLocked(nsecs_t currentTime, KeyEntry* entry,
        DropReason* dropReason, nsecs_t* nextWakeupTime) {
    ...

    // Give the policy a chance to intercept the key.
    if (entry->interceptKeyResult == KeyEntry::INTERCEPT_KEY_RESULT_UNKNOWN) {
        //派發給使用者,參考1.3.2.1
        if (entry->policyFlags & POLICY_FLAG_PASS_TO_USER) {
            //將InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible函式指標作為引數
            //執行postCommandLocked(),執行後 該函式被封裝到CommandEntry 加入到 mCommandQueue佇列
            CommandEntry* commandEntry = postCommandLocked(
                    & InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible);
            //InputWindowHandle儲存了視窗相關資訊,由java層而來
            //獲取焦點視窗的InputWindowHandle
            sp<InputWindowHandle> focusedWindowHandle =
                    getValueByKey(mFocusedWindowHandlesByDisplay, getTargetDisplayId(entry));
            if (focusedWindowHandle != nullptr) {
                //InputChannel也是一種跨程式
                commandEntry->inputChannel =
                    getInputChannelLocked(focusedWindowHandle->getToken());
            }
            commandEntry->keyEntry = entry;
            entry->refCount += 1;
            return false; // wait for the command to run
        } else {
            entry->interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_CONTINUE;
        }
    } 
    ...
    
    // Identify targets.
    //參考 1.3.2.3 
    std::vector<InputTarget> inputTargets;
    int32_t injectionResult = findFocusedWindowTargetsLocked(currentTime,
            entry, inputTargets, nextWakeupTime);
    ...
    // Add monitor channels from event's or focused display.
    addGlobalMonitoringTargetsLocked(inputTargets, getTargetDisplayId(entry));

    // Dispatch the key.
    //參考1.3.3
    dispatchEvenfentLocked(currentTime, entry, inputTargets);
    return true;
}

InputDispatcher::CommandEntry* InputDispatcher::postCommandLocked(Command command) {
    CommandEntry* commandEntry = new CommandEntry(command);
    mCommandQueue.enqueueAtTail(commandEntry);
    return commandEntry;
}
1.3.2.1 事件分派前的攔截:interceptKeyBeforeDispatching()

通過postCommandLocked() 將 doInterceptKeyBeforeDispatchingLockedInterruptible 函式作為引數,封裝到CommandEntry 最後加入到 mCommandQueue佇列。這個函式並沒有馬上執行。

這個doInterceptKeyBeforeDispatchingLockedInterruptible():

//InputDispatcher.cpp
void InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible(
        CommandEntry* commandEntry) {
    KeyEntry* entry = commandEntry->keyEntry;
    ...
    sp<IBinder> token = commandEntry->inputChannel != nullptr ?
        commandEntry->inputChannel->getToken() : nullptr;
    nsecs_t delay = mPolicy->interceptKeyBeforeDispatching(token,
            &event, entry->policyFlags);
    ...
    entry->release();
}

interceptKeyBeforeDispatching() 類似1.2.2.4。最終也是 呼叫到PhoneWindowManager 中的同名方法。

1.3.2.2 瞭解InputWindowHandle和InputChannel

InputWindowHandle:

InputWindowHandle儲存了視窗相關資訊,由java層而來。

關於InputWindowHandle 知道這大概是什麼,不影響此篇理解,就沒有完全跟蹤下去。下面個人檢視的路徑開始,內容挺多,也沒跟蹤完全。先記錄下,後續再看。

//WindowManagerService.java
public int addWindow(Session session, IWindow client, int seq, ...) {
    displayContent.getInputMonitor().updateInputWindowsLw(false /*force*/);
}

//InputMonitor.java
/* Updates the cached window information provided to the input dispatcher. */
void updateInputWindowsLw(boolean force) {
    if (!force && !mUpdateInputWindowsNeeded) {
        return;
    }
    scheduleUpdateInputWindows();
}

InputChannel:

InputChannel也是一種跨程式, 本質也是socket。是一對建立的。

WindowState建立了一對InputChannel。server端註冊到InputDispatcher,建立了Connect。client端返回給應用程式的視窗,ViewRootImpl.setView()時傳入的引數mInputChannel。

InputDispatcher向其InputChannel中寫入事件,視窗就可以從InputChannel中讀取了。

簡單列出下相關程式碼:

//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,
        InsetsState outInsetsState) {
    final boolean openInputChannels = (outInputChannel != null
            && (attrs.inputFeatures & INPUT_FEATURE_NO_INPUT_CHANNEL) == 0);
    if  (openInputChannels) {
        //outInputChannel來自 ViewRootImpl.setView()時建立的
        win.openInputChannel(outInputChannel);
    }
}

//WindowState.java
void openInputChannel(InputChannel outInputChannel) {
    if (mInputChannel != null) {
        throw new IllegalStateException("Window already has an input channel.");
    }
    String name = getName();
    //建立一對InputChannel,建立過程該篇不說明。         
    InputChannel[] inputChannels = InputChannel.openInputChannelPair(name);
    mInputChannel = inputChannels[0];
    mClientChannel = inputChannels[1];
    mInputWindowHandle.token = mClient.asBinder();
    if (outInputChannel != null) {
        //mClientChannel傳遞給 outInputChannel
        mClientChannel.transferTo(outInputChannel);
        mClientChannel.dispose();
        mClientChannel = null;
    } else {
        // If the window died visible, we setup a dummy input channel, so that taps
        // can still detected by input monitor channel, and we can relaunch the app.
        // Create dummy event receiver that simply reports all events as handled.
        mDeadWindowEventReceiver = new DeadWindowEventReceiver(mClientChannel);
    }
    //mInputChannel註冊到了InputDispatcher,註冊過程也不說明了。           
    mWmService.mInputManager.registerInputChannel(mInputChannel, mClient.asBinder());
}

//InputDispatcher.cpp
status_t InputDispatcher::registerInputChannel(const sp<InputChannel>& inputChannel,
        int32_t displayId) {
    { // acquire lock
        sp<Connection> connection = new Connection(inputChannel, false /*monitor*/);
        int fd = inputChannel->getFd();
        mConnectionsByFd.add(fd, connection);
        mInputChannelsByToken[inputChannel->getToken()] = inputChannel;
        mLooper->addFd(fd, 0, ALOOPER_EVENT_INPUT, handleReceiveCallback, this);
    } // release lock
}
1.3.2.3 確認目標

直接看 findFocusedWindowTargetsLocked() :

//InputDispatcher.cpp
int32_t InputDispatcher::findFocusedWindowTargetsLocked(nsecs_t currentTime,
        const EventEntry* entry, std::vector<InputTarget>& inputTargets, nsecs_t* nextWakeupTime) {
    int32_t injectionResult;
    std::string reason;
    int32_t displayId = getTargetDisplayId(entry);
    sp<InputWindowHandle> focusedWindowHandle =
            getValueByKey(mFocusedWindowHandlesByDisplay, displayId);
    sp<InputApplicationHandle> focusedApplicationHandle =
            getValueByKey(mFocusedApplicationHandlesByDisplay, displayId);
    ...
    // Success!  Output targets.
    injectionResult = INPUT_EVENT_INJECTION_SUCCEEDED;
    addWindowTargetLocked(focusedWindowHandle,
            InputTarget::FLAG_FOREGROUND | InputTarget::FLAG_DISPATCH_AS_IS, BitSet32(0),
            inputTargets);
    ...
    return injectionResult;
}

void InputDispatcher::addWindowTargetLocked(const sp<InputWindowHandle>& windowHandle,
        int32_t targetFlags, BitSet32 pointerIds, std::vector<InputTarget>& inputTargets) {
    sp<InputChannel> inputChannel = getInputChannelLocked(windowHandle->getToken());
    if (inputChannel == nullptr) {
        ALOGW("Window %s already unregistered input channel", windowHandle->getName().c_str());
        return;
    }
    const InputWindowInfo* windowInfo = windowHandle->getInfo();
    InputTarget target;
    target.inputChannel = inputChannel;
    target.flags = targetFlags;
    target.xOffset = - windowInfo->frameLeft;
    target.yOffset = - windowInfo->frameTop;
    target.globalScaleFactor = windowInfo->globalScaleFactor;
    target.windowXScale = windowInfo->windowXScale;
    target.windowYScale = windowInfo->windowYScale;
    target.pointerIds = pointerIds;
    inputTargets.push_back(target);
}

找到目標的InputWindowHandle,生成一個InputTarget 然後加入到inputTargets中。

InputTarget包含了視窗的各種資訊,如上可以仔細看下。

1.3.3 分派事件

直接看 dispatchEventLocked(),。

//InputDispatcher.cpp
void InputDispatcher::dispatchEventLocked(nsecs_t currentTime,
        EventEntry* eventEntry, const std::vector<InputTarget>& inputTargets) {
    ATRACE_CALL();
    ...
    pokeUserActivityLocked(eventEntry);

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

void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime,
        const sp<Connection>& connection, EventEntry* eventEntry, const InputTarget* inputTarget) {
    ...
    // Not splitting.  Enqueue dispatch entries for the event as is.
    enqueueDispatchEntriesLocked(currentTime, connection, eventEntry, inputTarget);
}

迴圈取出inputTargets的目標,一個個處理:

//InputDispatcher.cpp
void InputDispatcher::enqueueDispatchEntriesLocked(nsecs_t currentTime,
        const sp<Connection>& connection, EventEntry* eventEntry, const InputTarget* inputTarget) {
    ...
    bool wasEmpty = connection->outboundQueue.isEmpty();
    // Enqueue dispatch entries for the requested modes.
    enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,
            InputTarget::FLAG_DISPATCH_AS_HOVER_EXIT);
    enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,
            InputTarget::FLAG_DISPATCH_AS_OUTSIDE);
    enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,
            InputTarget::FLAG_DISPATCH_AS_HOVER_ENTER);
    enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,
            InputTarget::FLAG_DISPATCH_AS_IS);
    enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,
            InputTarget::FLAG_DISPATCH_AS_SLIPPERY_EXIT);
    enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,
            InputTarget::FLAG_DISPATCH_AS_SLIPPERY_ENTER);
    // If the outbound queue was previously empty, start the dispatch cycle going.
    if (wasEmpty && !connection->outboundQueue.isEmpty()) {
        startDispatchCycleLocked(currentTime, connection);
    }
}
void InputDispatcher::enqueueDispatchEntryLocked(
        const sp<Connection>& connection, EventEntry* eventEntry, const InputTarget* inputTarget,
        int32_t dispatchMode) {
    ...
    DispatchEntry* dispatchEntry = new DispatchEntry(eventEntry, // increments ref
            inputTargetFlags, inputTarget->xOffset, inputTarget->yOffset,
            inputTarget->globalScaleFactor, inputTarget->windowXScale,
            inputTarget->windowYScale);
    ...
    // Enqueue the dispatch entry.
    connection->outboundQueue.enqueueAtTail(dispatchEntry);
    traceOutboundQueueLength(connection);

}

Connection個人理解是一個通道。Connection中有一個outboundQueue佇列,上面將符合的事件封裝為DispatchEntry放到Connection的outboundQueue佇列中了。

然後接著看分派週期 startDispatchCycleLocked():

//InputDispatcher.cpp
void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime,
        const sp<Connection>& connection) {
    ...
    while (connection->status == Connection::STATUS_NORMAL
            && !connection->outboundQueue.isEmpty()) {
        //從connection的outboundQueue佇列取出一個元素
        DispatchEntry* dispatchEntry = connection->outboundQueue.head;
        dispatchEntry->deliveryTime = currentTime;

        // Publish the event.
        status_t status;
        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, keyEntry->displayId,
                    dispatchEntry->resolvedAction, dispatchEntry->resolvedFlags,
                    keyEntry->keyCode, keyEntry->scanCode,
                    keyEntry->metaState, keyEntry->repeatCount, keyEntry->downTime,
                    keyEntry->eventTime);
            break;
        }
        ...
        // Re-enqueue the event on the wait queue.
        connection->outboundQueue.dequeue(dispatchEntry);
        traceOutboundQueueLength(connection);
        connection->waitQueue.enqueueAtTail(dispatchEntry);
        traceWaitQueueLength(connection);
    }
}

//InputTransport.cpp
InputPublisher::InputPublisher(const sp<InputChannel>& channel) :
        mChannel(channel) {
}
status_t InputPublisher::publishKeyEvent(
       ...) {
    ...
    InputMessage msg;
    msg.header.type = InputMessage::TYPE_KEY;
    msg.body.key.seq = seq;
    msg.body.key.deviceId = deviceId;
    msg.body.key.source = source;
    msg.body.key.displayId = displayId;
    msg.body.key.action = action;
    msg.body.key.flags = flags;
    msg.body.key.keyCode = keyCode;
    msg.body.key.scanCode = scanCode;
    msg.body.key.metaState = metaState;
    msg.body.key.repeatCount = repeatCount;
    msg.body.key.downTime = downTime;
    msg.body.key.eventTime = eventTime;
    //通過InputChannel傳送訊息
    return mChannel->sendMessage(&msg);
}

迴圈取出Connection中outboundQueue佇列中的 事件依次處理分派。這裡還是 以按鍵 為例,通過 inputPublisher.publishKeyEvent() 分派了事件,即 最終是 將事件等資訊 封裝為InputMessage, 通過InputChannel將 這個訊息傳送出去。

//InputTransport.cpp
status_t InputChannel::sendMessage(const InputMessage* msg) {
    const size_t msgLength = msg->size();
    InputMessage cleanMsg;
    msg->getSanitizedCopy(&cleanMsg);
    ssize_t nWrite;
    do {
        //
        nWrite = ::send(mFd, &cleanMsg, msgLength, MSG_DONTWAIT | MSG_NOSIGNAL);
    } while (nWrite == -1 && errno == EINTR);
    ...
    return OK;
}

1.3.4 分派事件後 執行掛起命令

在InputDispatcher::dispatchOnce()中 ,前面講到的事件分派 都是 dispatchOnceInnerLocked()的執行,這個是 沒有掛起命令情況下執行的(即mCommandQueue為空)。

如果mCommandQueue非空,則會執行掛起的命令。如 在1.3.2.1中 攔截命令 被封裝 加入了mCommandQueue 佇列,然後分派就結束了。

如果mCommandQueue非空,會執行其中的命令,即 runCommandsLockedInterruptible() :

//InputDispatcher.cpp
bool InputDispatcher::runCommandsLockedInterruptible() {
    if (mCommandQueue.isEmpty()) {
        return false;
    }
    do {
        CommandEntry* commandEntry = mCommandQueue.dequeueAtHead();

        Command command = commandEntry->command;
        (this->*command)(commandEntry); // commands are implicitly 'LockedInterruptible'
        commandEntry->connection.clear();
        delete commandEntry;
    } while (! mCommandQueue.isEmpty());
    return true;
}

1.4 設定回撥:setWindowManagerCallbacks

在startOtherServices()中,建立了InputManagerService後,執行了 inputManager.setWindowManagerCallbacks(wm.getInputManagerCallback()); 。這個就是設定的回撥(即設定了mWindowManagerCallbacks),與前面提到的 兩個攔截有關,1.2.2.4已經說的比較明白了。這裡主要其中mWindowManagerCallbacks是什麼。

這句話很簡單,直接看下:

//WindowManagerService.java 
final InputManagerCallback mInputManagerCallback = new InputManagerCallback(this);
public InputManagerCallback getInputManagerCallback() {
    return mInputManagerCallback;
}

//InputManagerService.java
public void setWindowManagerCallbacks(WindowManagerCallbacks callbacks) {
    mWindowManagerCallbacks = callbacks;
}

這裡的 mWindowManagerCallbacks 就是 wm.getInputManagerCallback(),即 建立的InputManagerCallback物件。

1.5 啟動:start()

前面 InputReader讀取事件 和 InputDispatcher分派事件,這個過程是在 InputReaderThread和InputDispatcherThread 兩個執行緒執行起來,執行了 threadLoop() 基礎上講解的。

那麼 這兩個執行緒是如何 執行起來,執行 threadLoop() 的?下面就來看下。

1.5.1 進入native

在 startOtherServices()中,建立IMS後,設定了回撥,最後有 inputManager.start(); ,這個就是 兩個執行緒執行起來 並執行了 threadLoop() 的起點。

//InputManagerService.java
private static native void nativeStart(long ptr);

public void start() {
    ...
    nativeStart(mPtr);
    ...
}

這裡主要看下 nativeStart() 這個方法。 在 上面部分1.1 建立 InputManagerService中,大家還記得 mPtr 是:通過nativeInit()進入native建立 NativeInputManager 後的相關返回值(mPtr 是 reinterpret_cast(im),某一種轉換)。

繼續跟蹤下去。

//com_android_server_input_InputManagerService.cpp
static const JNINativeMethod gInputManagerMethods[] = {
    { "nativeStart", "(J)V", (void*) nativeStart },
}

static void nativeStart(JNIEnv* env, jclass /* clazz */, jlong ptr) {
    //mPtr又轉換成了NativeInputManager
    NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
    status_t result = im->getInputManager()->start();
    ...
}

mPtr又轉換成了NativeInputManager,然後呼叫了InputManager的 start()方法。 繼續看:

1.5.2 run() 操作

//InputManager.cpp
status_t InputManager::start() {
    status_t result = mDispatcherThread->run("InputDispatcher", PRIORITY_URGENT_DISPLAY);
    ...
    result = mReaderThread->run("InputReader", PRIORITY_URGENT_DISPLAY);
    ...
    return OK;
}

來看,這裡就是 InputReaderThread 和 InputDispatcherThread 兩個執行緒 執行了 run() 操作。 具體看下這個run() 做了些啥,需要看其父類Thread。

1.5.3 建立執行緒 並 執行 threadLoop()

//system/core/libutils/Threads.cpp
Thread::Thread(bool canCallJava)
    : mCanCallJava(canCallJava),
      ...
{
}
status_t Thread::run(const char* name, int32_t priority, size_t stack)
{
    Mutex::Autolock _l(mLock);
    ...
    bool res;
 	//InputReaderThread 和 InputDispatcherThread 建立時傳入的 為true。
    if (mCanCallJava) {
        //建立執行緒。 注意這裡的 _threadLoop
        res = createThreadEtc(_threadLoop,
                this, name, priority, stack, &mThread);
    } else {
        res = androidCreateRawThreadEtc(_threadLoop,
                this, name, priority, stack, &mThread);
    }
    ...
    return OK;
}

//frameworks/native/services/inputflinger/InputReaderBase.cpp
InputReaderThread::InputReaderThread(const sp<InputReaderInterface>& reader) :
        Thread(/*canCallJava*/ true), mReader(reader) {
}
//frameworks/native/services/inputflinger/InputDispatcher.cpp
InputDispatcherThread::InputDispatcherThread(const sp<InputDispatcherInterface>& dispatcher) :
        Thread(/*canCallJava*/ true), mDispatcher(dispatcher) {
}

這裡,mCanCallJava 是 true(建立 InputReaderThread和InputDispatcherThread時 傳入的,上面程式碼也列出),然後通過createThreadEtc() 即建立 執行緒。 注意其中有個引數 _threadLoop。下面是 _threadLoop。

//system/core/libutils/Threads.cpp
int Thread::_threadLoop(void* user)
{
    Thread* const self = static_cast<Thread*>(user);
    ...
    result = self->threadLoop();
    ...
    return 0;
}

這裡 就是 執行 自身的 threadLoop(),即 InputReaderThread 和 InputDispatcherThread 兩執行緒 執行 threadLoop()。

2. WMS中Window獲取事件和傳遞

IMS端講完了,我們知道最後訊息通過InputChannel傳送到目標視窗的程式了。接下來看目標視窗是如何接收傳遞的。

首先,來看下ViewRootImpl.setView() :

//ViewRootImpl.java
InputQueue.Callback mInputQueueCallback;
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
    synchronized (this) {
        if (mView == null) {
            mView = view;
            ...
            if ((mWindowAttributes.inputFeatures
                    & WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
                //建立InputChannel物件 mInputChannel
                mInputChannel = new InputChannel();
            }
            mForceDecorViewVisibility = (mWindowAttributes.privateFlags
                    & PRIVATE_FLAG_FORCE_DECOR_VIEW_VISIBILITY) != 0;
            try {
                ...
                //這裡主要關注mInputChannel,它就是前面講到的一個InputChannel,wms建立一對後傳遞回來的
                res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
                        getHostVisibility(), mDisplay.getDisplayId(), mTmpFrame,
                        mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
                        mAttachInfo.mOutsets, mAttachInfo.mDisplayCutout, mInputChannel,
                        mTempInsets);
                setFrame(mTmpFrame);
            } 
            ...
            if (mInputChannel != null) {
                if (mInputQueueCallback != null) {
                    mInputQueue = new InputQueue();
                    mInputQueueCallback.onInputQueueCreated(mInputQueue);
                }
                //建立WindowInputEventReceiver,這裡的Looper是應用主執行緒的Looper
                mInputEventReceiver = new WindowInputEventReceiver(mInputChannel,
                        Looper.myLooper());
            }
            ...
        }
    }
}

前面也提到過,這裡建立的mInputChannel 最終作為引數傳遞到WMS中,此時它什麼都沒有。在 WMS.addWindow()中 WindowState建立了一對InputChannel,其中一個通過transferTo()傳遞給了 mInputChannel。接下來就看WindowInputEventReceiver的建立。

2.1 WindowInputEventReceiver 的建立

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

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

    mCloseGuard.open("dispose");
}

通過nativeInit() 進入 native層:

//android_view_InputEventReceiver.cpp
static jlong nativeInit(JNIEnv* env, jclass clazz, jobject receiverWeak,
        jobject inputChannelObj, jobject messageQueueObj) {
    ...
    //參考2.2,建立NativeInputEventReceiver
    sp<NativeInputEventReceiver> receiver = new NativeInputEventReceiver(env,
            receiverWeak, inputChannel, messageQueue);
    //參考2.3,執行initialize
    status_t status = receiver->initialize();
    ...
    receiver->incStrong(gInputEventReceiverClassInfo.clazz); // retain a reference for the object
    return reinterpret_cast<jlong>(receiver.get());
}

2.2 建立NativeInputEventReceiver

//android_view_InputEventReceiver.cpp
class NativeInputEventReceiver : public LooperCallback {
NativeInputEventReceiver::NativeInputEventReceiver(JNIEnv* env,
        jobject receiverWeak, const sp<InputChannel>& inputChannel,
        const sp<MessageQueue>& messageQueue) :
        mReceiverWeakGlobal(env->NewGlobalRef(receiverWeak)),
        mInputConsumer(inputChannel), mMessageQueue(messageQueue),
        mBatchedInputEventPending(false), mFdEvents(0) {
    ...
}

InputConsumer::InputConsumer(const sp<InputChannel>& channel) :
        mResampleTouch(isTouchResamplingEnabled()),
        mChannel(channel), mMsgDeferred(false) {
}

建立NativeInputEventReceiver,注意兩個地方 後面會講到的:

  • inputChannel封裝到mInputConsumer
  • NativeInputEventReceiver是LooperCallback的派生類,實現了handleEvent()方法。

2.3 執行initialize()

//android_view_InputEventReceiver.cpp
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) { 
            //fd新增到Looper中,監聽InputChannel 讀取事件
            mMessageQueue->getLooper()->addFd(fd, 0, events, this, NULL);
        } else {
            mMessageQueue->getLooper()->removeFd(fd);
        }
    }
}

/**
* The file descriptor is available for read operations.
*/
ALOOPER_EVENT_INPUT = 1 << 0,

addFd()引數this是LooperCallback,即NativeInputEventReceiver。
跟蹤下,fd最終新增到Looper的mRequests列表中。

當Looper監聽到有輸入事件時,會回撥 NativeInputEventReceiver的handleEvent()方法。 (這裡面的機制也還沒細究)

這個可以參考下:Looper::pollInner()中 int callbackResult = response.request.callback->handleEvent(fd, events, data);

2.4 回撥handleEvent()

//android_view_InputEventReceiver.cpp
int NativeInputEventReceiver::handleEvent(int receiveFd, int events, void* data) {
    ...
    if (events & ALOOPER_EVENT_INPUT) {
        JNIEnv* env = AndroidRuntime::getJNIEnv();
        //獲取事件,然後回撥到java層的 InputEventReceiver.dispatchInputEvent
        status_t status = consumeEvents(env, false /*consumeBatches*/, -1, NULL);
    }
    ...
    return 1;
}

//android_view_InputEventReceiver.cpp
status_t NativeInputEventReceiver::consumeEvents(JNIEnv* env,
        bool consumeBatches, nsecs_t frameTime, bool* outConsumedBatch) {
    ...
    ScopedLocalRef<jobject> receiverObj(env, NULL);
    bool skipCallbacks = false;
    for (;;) {
        uint32_t seq;
        InputEvent* inputEvent;
        //從InputChannel讀取資訊,並處理儲存事件到inputEvent,參考2.4.1
        status_t status = mInputConsumer.consume(&mInputEventFactory,
                consumeBatches, frameTime, &seq, &inputEvent);
        ...
        if (!skipCallbacks) {
            ...
            if (inputEventObj) {
                ...
                //回撥java層的 InputEventReceiver.dispatchInputEvent,參考2.4.2
                env->CallVoidMethod(receiverObj.get(),
                        gInputEventReceiverClassInfo.dispatchInputEvent, seq, inputEventObj);
            } 
        }
        ...
    }
}

//jni註冊:android_view_InputEventReceiver.cpp
int register_android_view_InputEventReceiver(JNIEnv* env) {
    int res = RegisterMethodsOrDie(env, "android/view/InputEventReceiver",
            gMethods, NELEM(gMethods));
    jclass clazz = FindClassOrDie(env, "android/view/InputEventReceiver");
    gInputEventReceiverClassInfo.clazz = MakeGlobalRefOrDie(env, clazz);
    gInputEventReceiverClassInfo.dispatchInputEvent = GetMethodIDOrDie(env,
            gInputEventReceiverClassInfo.clazz,
            "dispatchInputEvent", "(ILandroid/view/InputEvent;)V");
    gInputEventReceiverClassInfo.dispatchBatchedInputEventPending = GetMethodIDOrDie(env,
            gInputEventReceiverClassInfo.clazz, "dispatchBatchedInputEventPending", "()V");
    return res;
}

NativeInputEventReceiver::handleEvent() 到 NativeInputEventReceiver::consumeEvents()。這裡關注兩個:

  • 從InputChannel讀取資訊,並處理儲存事件到inputEvent
  • 回撥java層的 InputEventReceiver.dispatchInputEvent

2.4.1 從InputChannel讀取事件

//InputTransport.cpp
status_t InputConsumer::consume(InputEventFactoryInterface* factory,
        bool consumeBatches, nsecs_t frameTime, uint32_t* outSeq, InputEvent** outEvent) {
    ...
    *outSeq = 0;
    *outEvent = nullptr;

    // Fetch the next input message.
    // Loop until an event can be returned or no additional events are received.
    while (!*outEvent) {
        //前面列出過InputConsumer建立時 mMsgDeferred為false
        if (mMsgDeferred) {
            ...
        } else {
            // Receive a fresh message.
            //mChannel接收訊息,即從socket中讀取
            status_t result = mChannel->receiveMessage(&mMsg);
            ...
        }
        ...
    }
    switch (mMsg.header.type) {
        case InputMessage::TYPE_KEY: {
            ...
            initializeKeyEvent(keyEvent, &mMsg);
            *outSeq = mMsg.body.key.seq;
            *outEvent = keyEvent;
            break;
        }

        case InputMessage::TYPE_MOTION: {
            ...
            updateTouchState(mMsg);
            initializeMotionEvent(motionEvent, &mMsg);
            *outSeq = mMsg.body.motion.seq;
            *outEvent = motionEvent;
            break;
        }
    }
    return OK;
}

通過InputChannel接受IMS端傳送過來的訊息,並且根據事件型別做了一些處理。

2.4.2 回撥java層的 InputEventReceiver.dispatchInputEvent

//InputEventReceiver.java
@UnsupportedAppUsage
private void dispatchInputEvent(int seq, InputEvent event) {
    mSeqMap.put(event.getSequenceNumber(), seq);
    onInputEvent(event);
}

前面知道,建立的是InputEventReceiver的子類WindowInputEventReceiver,因此onInputEvent()呼叫的是子類中方法:

//ViewRootImpl.java
final class WindowInputEventReceiver extends InputEventReceiver {
    @Override
    public void onInputEvent(InputEvent event) {
        ...
        if (processedEvents != null) {
            ...
        } else {
            //輸入事件 加入佇列
            enqueueInputEvent(event, this, 0, true);
        }
    }
}

2.5 輸入事件處理

這裡繼續看 enqueueInputEvent():

//ViewRootImpl.java
@UnsupportedAppUsage
void enqueueInputEvent(InputEvent event,
        InputEventReceiver receiver, int flags, boolean processImmediately) {
    //獲取QueuedInputEvent,event等封裝進去。
    QueuedInputEvent q = obtainQueuedInputEvent(event, receiver, flags);
    //獲取隊尾
    QueuedInputEvent last = mPendingInputEventTail;
    //插入隊尾
    if (last == null) {
        mPendingInputEventHead = q;
        mPendingInputEventTail = q;
    } else {
        last.mNext = q;
        mPendingInputEventTail = q;
    }
    //數目加1
    mPendingInputEventCount += 1;
    Trace.traceCounter(Trace.TRACE_TAG_INPUT, mPendingInputEventQueueLengthCounterName,
            mPendingInputEventCount);
    //是否立即執行
    if (processImmediately) {
        doProcessInputEvents();
    } else {
        scheduleProcessInputEvents();
    }
}

enqueueInputEvent() 首先將event等資訊封裝到了QueuedInputEvent,然後將其插入輸入事件佇列的隊尾。

繼續看doProcessInputEvents():

//ViewRootImpl.java
void doProcessInputEvents() {
    // Deliver all pending input events in the queue.
    while (mPendingInputEventHead != null) {
        QueuedInputEvent q = mPendingInputEventHead;
        ...
        deliverInputEvent(q);
    }
}       

迴圈處理佇列中所有事件,每次取隊首元素 傳遞處理。交由deliverInputEvent()方法處理。

2.6 輸入事件 傳遞到View

繼續看deliverInputEvent():

//ViewRootImpl.java
private void deliverInputEvent(QueuedInputEvent q) {
    ...
    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();
        //傳遞,參考3.1
        stage.deliver(q);
    } else {
        finishInputEvent(q);
    }
}
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
    // Set up the input pipeline.
    CharSequence counterSuffix = attrs.getTitle();
    mSyntheticInputStage = new SyntheticInputStage();
    InputStage viewPostImeStage = new ViewPostImeInputStage(mSyntheticInputStage);
    InputStage nativePostImeStage = new NativePostImeInputStage(viewPostImeStage,
            "aq:native-post-ime:" + counterSuffix);
    InputStage earlyPostImeStage = new EarlyPostImeInputStage(nativePostImeStage);
    InputStage imeStage = new ImeInputStage(earlyPostImeStage,
            "aq:ime:" + counterSuffix);
    InputStage viewPreImeStage = new ViewPreImeInputStage(imeStage);
    InputStage nativePreImeStage = new NativePreImeInputStage(viewPreImeStage,
            "aq:native-pre-ime:" + counterSuffix);

    mFirstInputStage = nativePreImeStage;
    mFirstPostImeInputStage = earlyPostImeStage;
    mPendingInputEventQueueLengthCounterName = "aq:pending:" + counterSuffix;
}

在setView() 中,建立了 input pipeline,將事件一層層傳遞下去。 呼叫stage.deliver(q); 傳遞下去。

3. 事件在View中的傳遞

前面講到 事件已經傳遞到input pipeline中。這個暫不細究,往下繼續看傳遞到View中的傳遞。

3.1 傳遞到DecorView

直接看 stage.deliver(q) :

//ViewRootImpl.java
abstract class InputStage {
    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));
        }
    }
}

onProcess(q)返回一個處理結果,apply根據這個結果再決定是否傳遞到InputStage的下一層。

這主要關注的 onProcess()。在ViewPostImeInputStage階段,開始向DecorView傳遞。

//ViewRootImpl.java
final class ViewPostImeInputStage extends InputStage {
    @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);
            }
        }
    }
    
    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;
    }
}

onProcess()中對不同型別事件進行不同的處理。這裡仍然以按鍵事件為例,處理方法processKeyEvent()。

這個mView即DecorView,setView()時 傳入的。

為什麼是DecorView? 這個過程請參考: Android10_原理機制系列_Activity視窗新增到WMS過程。


3.2 傳遞到Activity

//DecorView.java
@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);
}

如果是Activity的視窗,cb獲取到的是Activity,mFeatureId是-1。這裡的mWindow是PhoneWindow,即Activity在attach()時 建立的PhoneWindow,在setContentView()過程 通過mDecor.setWindow()傳入到DecorView中的。

這個mWindow.getCallback()獲取的是Activity本身,即Activity在attach()時setCallback() 傳入的this本身。

這個過程請參考( 那篇視窗新增到WMS中 說的很明白,這裡不列出了): Android10_原理機制系列_Activity視窗新增到WMS過程。


3.3 事件在View中傳遞處理

由於按鍵事件 和 觸控事件是 最常見的,這裡都簡單列舉了下。

3.3.1 按鍵事件傳遞處理

接著前面,按鍵事件 可以直接看cb.dispatchKeyEvent(event):

//Activity.java
public boolean dispatchKeyEvent(KeyEvent event) {
    ...
    Window win = getWindow();
    //交由Window繼續傳遞,返回false,則繼續交由Activity處理。若返回的true,則下層已處理掉了。          
    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);
}
//PhoneWindow.java
@Override
public boolean superDispatchKeyEvent(KeyEvent event) {
    //傳遞給DecorView
    return mDecor.superDispatchKeyEvent(event);
}
//DecorView.java
public boolean superDispatchKeyEvent(KeyEvent event) {
    ...
    //傳遞到ViewGroup。返回true,則下層處理了 上層不處理。  
    if (super.dispatchKeyEvent(event)) {
        return true;
    }

    return (getViewRootImpl() != null) && getViewRootImpl().dispatchUnhandledKeyEvent(event);
}
//ViewGroup.java
@Override
public boolean dispatchKeyEvent(KeyEvent event) {
    if ((mPrivateFlags & (PFLAG_FOCUSED | PFLAG_HAS_BOUNDS))
            == (PFLAG_FOCUSED | PFLAG_HAS_BOUNDS)) {
        //傳遞給具體的View
        if (super.dispatchKeyEvent(event)) {
            return true;
        }
    } else if (mFocused != null && (mFocused.mPrivateFlags & PFLAG_HAS_BOUNDS)
            == PFLAG_HAS_BOUNDS) {
        if (mFocused.dispatchKeyEvent(event)) {
            return true;
        }
    }
    return false;
}
//View.java
public boolean dispatchKeyEvent(KeyEvent event) {
    ...
    // Give any attached key listener a first crack at the event.
    //noinspection SimplifiableIfStatement
    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;
    }
    return false;
}
//KeyEvent.java
public final boolean dispatch(Callback receiver, DispatcherState state,
		Object target) {
	switch (mAction) {
		case ACTION_DOWN: {
			mFlags &= ~FLAG_START_TRACKING;
			boolean res = receiver.onKeyDown(mKeyCode, this);
			...
			return res;
		}
		case ACTION_UP:
			...
			return receiver.onKeyUp(mKeyCode, this);
		case ACTION_MULTIPLE:
			...
			return false;
	}
	return false;
}

由上述程式碼過程,keyEvent由外到內傳遞,由Activity到具體的View。

ListenerInfo就是關聯的我們自定義的監聽,如setOnClickListener(),setOnLongClickListener。

這裡的傳遞是:由Activity到ViewGrop再到View,如果某個環節返回true,即事件被處理掉不再向下層傳遞。如果最底層View仍未處理 而返回false,則再依次向外傳遞至Activity(向外傳遞中仍未被處理的話)處理。

注意:dispatchKeyEvent()都是有的。 onKeyDown,onKeyUp、onKeyLongPress等是View中有,同樣為true即處理掉 不在傳遞了。

3.3.2 觸控事件傳遞處理

觸控事件,類似按鍵事件,這裡直接看 最終傳遞到的Activity的地方。

//Activity.java
public boolean dispatchTouchEvent(MotionEvent ev) {
    if (ev.getAction() == MotionEvent.ACTION_DOWN) {
        onUserInteraction();
    }
    //傳遞到Window
    if (getWindow().superDispatchTouchEvent(ev)) {
        return true;
    }
    //若下層不處理,則呼叫onTouchEvent()處理掉。
    return onTouchEvent(ev);
}
//PhoneWindow.java
public boolean superDispatchTouchEvent(MotionEvent event) {
    return mDecor.superDispatchTouchEvent(event);
}
//DecorView.java
public boolean superDispatchTouchEvent(MotionEvent event) {
    return super.dispatchTouchEvent(event);
}
//ViewGroup.java
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
    ...
    boolean handled = false;
    if (onFilterTouchEventForSecurity(ev)) {
        ...
        // Check for interception.
        final boolean intercepted;
        ...
            	//攔截
                intercepted = onInterceptTouchEvent(ev);
        ...
        
        if (intercepted || mFirstTouchTarget != null) {
            ev.setTargetAccessibilityFocus(false);
        }
        //Update list of touch targets for pointer down, if needed.
        if (!canceled && !intercepted) {
            ...
        }

        // Dispatch to touch targets.
        if (mFirstTouchTarget == null) {
            // No touch targets so treat this as an ordinary view.
            handled = dispatchTransformedTouchEvent(ev, canceled, null,
                    TouchTarget.ALL_POINTER_IDS);
        } else {
				...
                        if (dispatchTransformedTouchEvent(ev, cancelChild,
                                target.child, target.pointerIdBits)) {
                            handled = true;
                        }
        }
    }
    return handled;
}

private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,
        View child, int desiredPointerIdBits) {
    ...
    // Perform any necessary transformations and dispatch.
    if (child == null) {
        handled = super.dispatchTouchEvent(transformedEvent);
    } else {
        ...
        handled = child.dispatchTouchEvent(transformedEvent);
    }
    return handled;
}
//View.java
public boolean dispatchTouchEvent(MotionEvent event) {
    ...
    if (onFilterTouchEventForSecurity(event)) {
        if ((mViewFlags & ENABLED_MASK) == ENABLED && handleScrollBarDragging(event)) {
            result = true;
        }
        //noinspection SimplifiableIfStatement
        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);
    }
    ...
    return result;
}

與按鍵事件類似,由外到內傳遞。也有ListenerInfo關聯View中自定義的監聽。

傳遞過程也基本一樣:由Activity到ViewGrop再到View,如果某個環節返回true,即事件被處理掉不再向下層傳遞。如果最底層View仍未處理 而返回false,則再依次向外傳遞至Activity(向外傳遞中仍未被處理)處理。

注意:dispatchTouchEvent(),onTouchEvent()都是有的。 ViewGroup中多了個onInterceptTouchEvent(),若為true, 則是將事件攔截,不在傳遞。


到此結束了,本篇差不多有上萬字了,但也只是個大概。仍需不斷學習。
多謝閱讀,歡迎交流。


相關文章