前言和概述
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的啟動過程。
若有不對,歡迎指出:
說明:
- 圖中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
繼續跟蹤下去。
//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, 則是將事件攔截,不在傳遞。
到此結束了,本篇差不多有上萬字了,但也只是個大概。仍需不斷學習。
多謝閱讀,歡迎交流。