本文轉(zhuǎn)載自:
1.Android輸入系統(tǒng)
??說到屏幕點(diǎn)擊事件喧枷,大部分同學(xué)最早想到的就是自定義中點(diǎn)擊事件的處理過程弯院,屏幕滑動,Down事件栗菜,Up事件等。自定義View的事件處理其實(shí)在整個(gè)Android輸入系統(tǒng)中只能算是最上層的衬鱼。 輸入系統(tǒng)如果按結(jié)構(gòu)分層可以分為:
- 輸入系統(tǒng)部分
??包含輸入子系統(tǒng)以及InputManagerService宙橱,用于事件的捕獲以及分發(fā)給上一級。
- WindowManagerService處理部分
??輸入系統(tǒng)部分將事件分發(fā)給對應(yīng)的Window柬祠,而Window正是由WMS來管理的北戏。
- View處理部分
??就是Activity->ViewGroup->View層面的事件分發(fā)的邏輯。
??這里再提供一張更加詳細(xì)的Android輸入系統(tǒng)模型:
??輸入系統(tǒng)說白了就是捕獲事件漫蛔,并將事件分發(fā)給WMS進(jìn)行處理嗜愈。關(guān)鍵字:事件捕獲旧蛾,事件分發(fā)。
1.1 輸入子系統(tǒng)
??Android中的輸入設(shè)備有很多種蠕嫁,如:鍵盤锨天,屏幕,鼠標(biāo)等剃毒,開發(fā)中最常見的就是屏幕和按鍵(如Home鍵等屬于鍵盤)了绍绘。
??這些設(shè)備對于核心處理器來說就是一個(gè)“即插即用”外設(shè),和我們電腦插入鼠標(biāo)或手機(jī)后迟赃,在“設(shè)備管理器”里面會新增一個(gè)輸入設(shè)備節(jié)點(diǎn)一樣(前提是已經(jīng)安裝好對應(yīng)的驅(qū)動) 陪拘。
??Android處理器在接入這些“外設(shè)”后,比如滑動屏幕纤壁,設(shè)備驅(qū)動層就會接受到原始事件最終將事件傳遞到用戶空間的設(shè)備節(jié)點(diǎn)(dev/input/)中左刽。Android提供了一些api可以讓開發(fā)者在設(shè)備節(jié)點(diǎn)(dev/input/)中讀取內(nèi)核寫入的事件。
1.2 InputManagerService
??InputManagerService(輸入管理服務(wù))簡稱IMS酌媒,在安卓系統(tǒng)中負(fù)責(zé)它管理整個(gè)系統(tǒng)的輸入部分欠痴,包括鍵盤、鼠標(biāo)秒咨、觸摸屏等等喇辽,它與WindowManager密切相關(guān)。它讀取設(shè)備節(jié)點(diǎn)(dev/input/)中的輸入事件雨席,并對數(shù)據(jù)進(jìn)行二次甚至三次加工后分發(fā)給合適的Window進(jìn)行處理菩咨。IMS整體啟動過程和重要方法如下圖所示。
2.事件讀取機(jī)制
??我們以IMS為入口陡厘。在設(shè)備開機(jī)過程中抽米,啟動SystemServer部分初始了很多系統(tǒng)服務(wù),這里面就包括了IMS的創(chuàng)建和啟動過程糙置。 注:文章使用的源碼版本為8.0云茸。以上就是整個(gè)輸入事件讀取的過程:
2.1 IMS的創(chuàng)建與啟動
??在SystemServer的run方法中調(diào)用了startOtherServices。
//frameworks/base/services/java/com/android/server/SystemServer.java
private void startOtherServices() {
...
inputManager = new InputManagerService(context);//1.創(chuàng)建IMS
...
wm = WindowManagerService.main(context, inputManager,
mFactoryTestMode != FactoryTest.FACTORY_TEST_LOW_LEVEL,
!mFirstBoot, mOnlyCore, new PhoneWindowManager());//2.IMS傳入WMS
...
inputManager.start();//3.啟動IMS
}
注釋1:調(diào)用了IMS的構(gòu)造方法谤饭;
注釋2:將IMS作為參數(shù)傳遞給WMS标捺;
注釋3:調(diào)用了IMS的啟動方法start。
??我們來具體分析下注釋1和注釋3:注釋1
public InputManagerService(Context context) {
...
mPtr = nativeInit(this, mContext, mHandler.getLooper().getQueue());
...
}
重點(diǎn)看nativeInit方法揉抵,這是一個(gè)native方法亡容。
//frameworks\base\services\core\jni\com_android_server_input_InputManagerService.cpp
static jlong nativeInit(JNIEnv* env, jclass /* clazz */,
jobject serviceObj, jobject contextObj, jobject messageQueueObj) {
...
NativeInputManager* im = new NativeInputManager(contextObj, serviceObj,
messageQueue->getLooper());
im->incStrong(0);
return reinterpret_cast<jlong>(im);//返回NativeInputManager指針到j(luò)ava framework層
}
//------------------------------------
NativeInputManager::NativeInputManager(jobject contextObj,
jobject serviceObj, const sp<Looper>& looper) :
mLooper(looper), mInteractive(true) {
JNIEnv* env = jniEnv();
mContextObj = env->NewGlobalRef(contextObj);//1.將java層的傳下來的Context上下文保存在mContextObj中
mServiceObj = env->NewGlobalRef(serviceObj);//2.將java層傳遞下來的InputManagerService對象保存在mServiceObj中
...
sp<EventHub> eventHub = new EventHub();//3.創(chuàng)建EventHub
mInputManager = new InputManager(eventHub, this, this);//4.創(chuàng)建InputManager
}
nativeInit方法中創(chuàng)建了一個(gè)NativeInputManager對象,并將該對象指針返回給了java framework層功舀。 這就是為了打通java和native層萍倡,下次需要使用native層的NativeInputManager對象的時(shí)候,直接傳遞這個(gè)指針就可以訪問了辟汰。
??繼續(xù)看NativeInputManager構(gòu)造方法:
- 注釋1:將java層的傳下來的Context上下文保存在mContextObj中列敲;
- 注釋2:將java層傳遞下來的InputManagerService對象保存在mServiceObj中阱佛。
??如果你對源碼比較熟悉,可以知道大部分native層和java層的交互都是通過這個(gè)模式戴而,調(diào)用模型圖如下:
NativeInputManager構(gòu)造方法注釋3處:創(chuàng)建一個(gè)EventHub凑术,EventHub通過Linux內(nèi)核的INotify和epoll機(jī)制監(jiān)聽設(shè)備節(jié)點(diǎn),使用它的getEvent函數(shù)就可以讀取設(shè)備節(jié)點(diǎn)的原始事件以及設(shè)備節(jié)點(diǎn)增刪事件所意。 這里說明下原始事件和設(shè)備增刪事件:
原始事件:比如鍵盤節(jié)點(diǎn)的輸入事件或者屏幕的觸屏DOWN或者UP事件等淮逊,都屬于原始事件。
設(shè)備增刪事件:值鍵盤或者屏幕等節(jié)點(diǎn)的增刪扶踊,EventHub在獲取這類事件后泄鹏,會在native層創(chuàng)建對應(yīng)的節(jié)點(diǎn)處理Mapper對象。
NativeInputManager構(gòu)造方法注釋4處:創(chuàng)建一個(gè)InputManager對象并將注釋3處的EventHub對象作為參數(shù)傳入秧耗。 進(jìn)入InputManager構(gòu)造方法:
InputManager::InputManager(
const sp<EventHubInterface>& eventHub,
const sp<InputReaderPolicyInterface>& readerPolicy,
const sp<InputDispatcherPolicyInterface>& dispatcherPolicy) {
mDispatcher = new InputDispatcher(dispatcherPolicy);//1
mReader = new InputReader(eventHub, readerPolicy, mDispatcher);//2
initialize();//3
}
//----------------------------------------
void InputManager::initialize() {
mReaderThread = new InputReaderThread(mReader);
mDispatcherThread = new InputDispatcherThread(mDispatcher);
}
InputManager構(gòu)造方法中:
創(chuàng)建InputDispatcher類對象mDispatcher备籽,InputDispatcher類主要用來對原始事件進(jìn)行分發(fā),傳遞給WMS分井;
創(chuàng)建InputReader類對象mReader车猬,并傳入1中mDispatcher對象以及eventHub對象, 為什么要傳入這2個(gè)對象呢尺锚?因?yàn)镮nputReader機(jī)制就是:eventHub對象用來讀取事件數(shù)據(jù)珠闰,mDispatcher對象用來將讀取到事件數(shù)據(jù)分發(fā);
initialize方法中創(chuàng)建了InputReaderThread對象和InputDispatcherThread對象 因?yàn)槭录x取機(jī)制是一個(gè)耗時(shí)過程瘫辩,不能在主線程中進(jìn)行伏嗜,所以使用InputReaderThread線程來讀取事件,用InputDispatcherThread線程來分發(fā)事件杭朱。
??關(guān)于IMS的構(gòu)造方法就講了這么多阅仔,先來小結(jié)下:
IMS構(gòu)造方法中:創(chuàng)建了一個(gè)NativeInputManager的native對象吹散,并將java層的Context上下文保存在native層的mContextObj弧械,將java層的IMS對象保存在native層的mServiceObj中 創(chuàng)建InputManager對象并傳入一個(gè)新建的EventHub對象,用于讀取設(shè)備節(jié)點(diǎn)事件空民。
InputManager構(gòu)造方法中:創(chuàng)建了一個(gè)InputDispatcher和InputReader對象刃唐,以及用于讀取事件的InputReaderThread線程和分發(fā)事件的InputDispatcherThread線程。
??下面我們繼續(xù)看IMS的啟動方法界轩,在startOtherServices方法的調(diào)用inputManager.start画饥。
//frameworks/base/services/java/com/android/server/SystemServer.java
private void startOtherServices() {
...
inputManager = new InputManagerService(context);//1.創(chuàng)建IMS
...
wm = WindowManagerService.main(context, inputManager,
mFactoryTestMode != FactoryTest.FACTORY_TEST_LOW_LEVEL,
!mFirstBoot, mOnlyCore, new PhoneWindowManager());//2.IMS傳入WMS
...
inputManager.start();//3.啟動IMS
}
start方法中主要調(diào)用了nativeStart方法,參數(shù)為初始化時(shí)浊猾,native返回的NativeInputManager對象地址抖甘。
static void nativeStart(JNIEnv* env, jclass /* clazz */, jlong ptr) {
NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
status_t result = im->getInputManager()->start();
if (result) {
jniThrowRuntimeException(env, "Input manager could not be started.");
}
}
nativeStart方法調(diào)用了NativeInputManager的InputManager的start方法。
status_t InputManager::start() {
status_t result = mDispatcherThread->run("InputDispatcher", PRIORITY_URGENT_DISPLAY);
...
result = mReaderThread->run("InputReader", PRIORITY_URGENT_DISPLAY);
if (result) {
ALOGE("Could not start InputReader thread due to error %d.", result);
mDispatcherThread->requestExit();
return result;
}
return OK;
}
start方法主要作用就是啟動初始化時(shí)創(chuàng)建的兩個(gè)線程:mDispatcherThread和mReaderThread葫慎,這里注意先后順序先啟動事件分發(fā)線程衔彻,再啟動事件讀取線程薇宠。這是為了在事件讀取后可以立即對事件進(jìn)行分發(fā)。
??IMS啟動時(shí)序圖如下:
2.2 線程的創(chuàng)建與啟動
??在分析兩個(gè)線程啟動過程之前艰额,我們先來講解下Thread的run方法澄港。
//system\core\libutils\Threads.h
status_t Thread::run(const char* name, int32_t priority, size_t stack)
{
...
res = createThreadEtc(_threadLoop,
this, name, priority, stack, &mThread);
...
}
Thread的run方法中調(diào)用了createThreadEtc,這個(gè)方法第一個(gè)參數(shù)_threadLoop是一個(gè)方法指針柄沮,第二個(gè)參數(shù)是自己回梧,最終會調(diào)用到_threadLoop方法并傳入this指針。
int Thread::_threadLoop(void* user)
Thread* const self = static_cast<Thread*>(user);
...
do {
bool result;
result = self->threadLoop();
...
if (result == false || self->mExitPending) {
...
break;
}
...
} while(strong != 0);
}
_threadLoop方法內(nèi)部會調(diào)用self->threadLoop()祖搓,這個(gè)self就是當(dāng)前Thread的this指針狱意,除了result返回false或者調(diào)用了requestExit才會退出。 不然會一直循環(huán)調(diào)用self->threadLoop()函數(shù)拯欧,threadLoop在Thread中是一個(gè)純虛函數(shù)髓涯,在其子類中實(shí)現(xiàn)。
2.2.1 InputDispatcher/InputDispatcherThread
??所以后面只要分析子線程的threadLoop方法即可哈扮,下面我們先來分析InputDispatcherThread:
bool InputDispatcherThread::threadLoop() {
mDispatcher->dispatchOnce();
return true;
}
這里的mDispatcher是InputDispatcher類對象纬纪。
void InputDispatcher::dispatchOnce() {
nsecs_t nextWakeupTime = LONG_LONG_MAX;
{ // acquire lock
..
if (!haveCommandsLocked()) {//1.判斷有沒有命令需要執(zhí)行
dispatchOnceInnerLocked(&nextWakeupTime);//2.分發(fā)事件
}
if (runCommandsLockedInterruptible()) {//3.執(zhí)行命令
nextWakeupTime = LONG_LONG_MIN;//4.設(shè)置下次喚醒的時(shí)間
}
} // release lock
...
mLooper->pollOnce(timeoutMillis);//5.讓線程進(jìn)入休眠
}
注釋1:判斷有沒有命令需要執(zhí)行,如果沒有命令滑肉,則調(diào)用注釋2的dispatchOnceInnerLocked分發(fā)事件包各;如果有命令,則在注釋3處執(zhí)行完所有命令后靶庙,將nextWakeupTime置為LONG_LONG_MIN问畅,這樣就可以讓下一次線程可以被立即喚醒。 注釋5處調(diào)用mLooper->pollOnce六荒,讓線程進(jìn)入休眠护姆。第一次啟動的時(shí)候是直接進(jìn)入休眠,等待事件的到來掏击。
2.2.2 InputReader/InputReaderThread
??下面再來分析事件讀取線程InputReaderThread:
bool InputReaderThread::threadLoop() {
mReader->loopOnce();
return true;
}
這里mReader是InputReader類對象:
void InputReader::loopOnce() {
...
size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);//1.獲取事件
{ // acquire lock
...
if (count) {
processEventsLocked(mEventBuffer, count);//2.處理事件
}
...
} // release lock
...
mQueuedListener->flush();//3
}
2.3 事件的獲取與賦值
注釋1處調(diào)用EventHub的getEvents方法讀取設(shè)備節(jié)點(diǎn)中的輸入事件卵皂,注意這個(gè)方法內(nèi)部在沒有輸入事件的時(shí)候也是一個(gè)休眠的過程,并不是死循環(huán)耗時(shí)操作砚亭。
在注釋2處如果count不為0灯变,說明有事件,調(diào)用processEventsLocked方法捅膘。
2.4 事件的處理與加工
??進(jìn)入processEventsLocked看看:
void InputReader::processEventsLocked(const RawEvent* rawEvents, size_t count) {
for (const RawEvent* rawEvent = rawEvents; count;) {
int32_t type = rawEvent->type;
if (type < EventHubInterface::FIRST_SYNTHETIC_EVENT) {//1.原始事件類型
...
processEventsForDeviceLocked(deviceId, rawEvent, batchSize);
} else {//2.設(shè)備節(jié)點(diǎn)事件
switch (rawEvent->type) {
case EventHubInterface::DEVICE_ADDED:
addDeviceLocked(rawEvent->when, rawEvent->deviceId);//添加設(shè)備
break;
case EventHubInterface::DEVICE_REMOVED:
removeDeviceLocked(rawEvent->when, rawEvent->deviceId);//刪除設(shè)備
break;
case EventHubInterface::FINISHED_DEVICE_SCAN:
handleConfigurationChangedLocked(rawEvent->when);//設(shè)備配置改變
break;
default:
ALOG_ASSERT(false); // can't happen
break;
}
}
count -= batchSize;
}
}
processEventsLocked方法主要實(shí)現(xiàn)了:根據(jù)事件的type類型進(jìn)行不同處理添祸。
原始事件:調(diào)用processEventsForDeviceLocked處理。
設(shè)備節(jié)點(diǎn)事件:調(diào)用節(jié)點(diǎn)的添加和刪除等操作寻仗。
??我們先來看原始事件:processEventsForDeviceLocked
void InputReader::processEventsForDeviceLocked(int32_t deviceId,
const RawEvent* rawEvents, size_t count) {
ssize_t deviceIndex = mDevices.indexOfKey(deviceId);//獲取設(shè)備在mDevices中的索引
...
InputDevice* device = mDevices.valueAt(deviceIndex);//根據(jù)索引獲取InputDevice對象
...
device->process(rawEvents, count);//調(diào)用InputDevice的process方法繼續(xù)處理
}
根據(jù)deviceId去獲取設(shè)備在mDevices中的索引刃泌,根據(jù)索引獲取InputDevice對象。然后調(diào)用InputDevice的process方法繼續(xù)處理。
void InputDevice::process(const RawEvent* rawEvents, size_t count) {
...
size_t numMappers = mMappers.size();
for (const RawEvent* rawEvent = rawEvents; count--; rawEvent++) {
...
...
for (size_t i = 0; i < numMappers; i++) {
InputMapper* mapper = mMappers[i];
mapper->process(rawEvent);
}
...
...
}
}
process方法最終是使用不同的InputMapper進(jìn)行處理耙替, 那這個(gè)InputMapper在哪里設(shè)置成員呢鲤遥。
??我們回到前面processEventsLocked方法:如果是節(jié)點(diǎn)處理事件,如添加則調(diào)用addDeviceLocked方法, addDeviceLocked方法中又調(diào)用了createDeviceLocked方法林艘,并將返回的InputDevice放入到mDevices列表中盖奈。
void InputReader::addDeviceLocked(nsecs_t when, int32_t deviceId) {
...
InputDevice* device = createDeviceLocked(deviceId, controllerNumber, identifier, classes);
...
mDevices.add(deviceId, device);
...
}
//--------------------------------------
InputDevice* InputReader::createDeviceLocked(int32_t deviceId, int32_t controllerNumber,
const InputDeviceIdentifier& identifier, uint32_t classes) {
InputDevice* device = new InputDevice(&mContext, deviceId, bumpGenerationLocked(),
controllerNumber, identifier, classes);
...
// Switch-like devices.
if (classes & INPUT_DEVICE_CLASS_SWITCH) {
device->addMapper(new SwitchInputMapper(device));
}
// Scroll wheel-like devices.
if (classes & INPUT_DEVICE_CLASS_ROTARY_ENCODER) {
device->addMapper(new RotaryEncoderInputMapper(device));
}
...
// Keyboard-like devices.
...
if (keyboardSource != 0) {
device->addMapper(new KeyboardInputMapper(device, keyboardSource, keyboardType));
}
...
// Touchscreens and touchpad devices.
if (classes & INPUT_DEVICE_CLASS_TOUCH_MT) {
device->addMapper(new MultiTouchInputMapper(device));
} else if (classes & INPUT_DEVICE_CLASS_TOUCH) {
device->addMapper(new SingleTouchInputMapper(device));
}
...
return device;
}
可以看到createDeviceLocked根據(jù)不同輸入類型給Device設(shè)備添加了不同的InputMapper。
??回到前面InputDevice::process方法中:這里用KeyboardInputMapper來做例子狐援。 這個(gè)方法調(diào)用了KeyboardInputMapper的process方法钢坦。
void KeyboardInputMapper::process(const RawEvent* rawEvent) {
switch (rawEvent->type) {
case EV_KEY: {
..
if (isKeyboardOrGamepadKey(scanCode)) {
processKey(rawEvent->when, rawEvent->value != 0, scanCode, usageCode);
}
break;
}
}
}
//--------------------------------------
void KeyboardInputMapper::processKey(nsecs_t when, bool down, int32_t scanCode,
int32_t usageCode) {
...
NotifyKeyArgs args(when, getDeviceId(), mSource, policyFlags,
down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP,
AKEY_EVENT_FLAG_FROM_SYSTEM, keyCode, scanCode, keyMetaState, downTime);
getListener()->notifyKey(&args);
}
最終在processKey方法中將原始事件封裝為一個(gè)新的NotifyKeyArgs對象,并調(diào)用 getListener()->notifyKey方法作為參數(shù)傳入啥酱,這里getListener是在InputReader構(gòu)造方法中初始化爹凹。
InputReader::InputReader(const sp<EventHubInterface>& eventHub,
const sp<InputReaderPolicyInterface>& policy,
const sp<InputListenerInterface>& listener) :
mContext(this), mEventHub(eventHub), mPolicy(policy),
mGlobalMetaState(0), mGeneration(1),
mDisableVirtualKeysTimeout(LLONG_MIN), mNextTimeout(LLONG_MAX),
mConfigurationChangesToRefresh(0) {
mQueuedListener = new QueuedInputListener(listener);
...
}
就是這個(gè)mQueuedListener,而這個(gè)listener是外部傳入的InputDispatcher對象镶殷。
??那進(jìn)入QueuedInputListener的notifyKey看看:
void QueuedInputListener::notifyKey(const NotifyKeyArgs* args) {
mArgsQueue.push(new NotifyKeyArgs(*args));
}
這里只是將NotifyKeyArgs事件對象存儲到mArgsQueue隊(duì)列中禾酱,并沒有真正對事件進(jìn)行處理,那什么時(shí)候處理呢绘趋? 觀察仔細(xì)的同學(xué)應(yīng)該看到在InputReader的loopOnce中調(diào)用了mQueuedListener->flush()颤陶。
void InputReader::loopOnce() {
...
mQueuedListener->flush();//3
}
//---------------------------------
void QueuedInputListener::flush() {
size_t count = mArgsQueue.size();
for (size_t i = 0; i < count; i++) {
NotifyArgs* args = mArgsQueue[i];
args->notify(mInnerListener);
delete args;
}
mArgsQueue.clear();
}
這個(gè)flush方法就是循環(huán)調(diào)用列表中的事件,并依次執(zhí)行NotifyArgs的notify方法傳入的參數(shù)mInnerListener為初始化時(shí)的InputDispatcher對象陷遮。
2.5 事件的分發(fā)與通知
??繼續(xù)進(jìn)入NotifyArgs的notify方法:
struct NotifyArgs {
virtual void notify(const sp<InputListenerInterface>& listener) const = 0;
}
notify方法是一個(gè)純虛函數(shù)滓走,由其子類實(shí)現(xiàn)。這里子類就是NotifyKeyArgs對象帽馋。
void NotifyKeyArgs::notify(const sp<InputListenerInterface>& listener) const {
listener->notifyKey(this);
}
這里listener是InputDispatcher的對象搅方。
2.5.1 InputDispatcher/InputDispatcherThread
void InputDispatcher::notifyKey(const NotifyKeyArgs* args) {
...
bool needWake;
{ // acquire lock
...
KeyEntry* newEntry = new KeyEntry(args->eventTime,
args->deviceId, args->source, policyFlags,
args->action, flags, keyCode, args->scanCode,
metaState, repeatCount, args->downTime);//1.封裝成KeyEntry
needWake = enqueueInboundEventLocked(newEntry); //2.將KeyEntry對象壓入mInboundQueue中
} // release lock
if (needWake) {
mLooper->wake();//3.喚醒InputDispatcherThread線程進(jìn)行處理
}
}
注釋1處:將NotifyKeyArgs事件重新封裝為一個(gè)KeyEntry對象。
注釋2處:enqueueInboundEventLocked將KeyEntry對象壓入mInboundQueue中绽族。
注釋3處:喚醒InputDispatcherThread線程進(jìn)行處理姨涡。
??到這里InputReader的輸入事件讀取流程已經(jīng)全部走完。
??事件讀取時(shí)序圖如下:
2.6 事件獲取流程小結(jié)
SystemServer創(chuàng)建并啟動InputManagerService吧慢;
InputManagerService在native層創(chuàng)建一個(gè)NativeInputManager對象涛漂;
NativeInputManager內(nèi)部創(chuàng)建一個(gè)InputManager對象;
InputManager啟動InputReaderThread和InputDispatcherThread娄蔼;
在InputReaderThread線程中調(diào)用EventHub的getEvents獲取設(shè)備節(jié)點(diǎn)中的輸入事件怖喻;
并將輸入事件封裝為NotifyKeyArgs對象放入隊(duì)列中;
之后再調(diào)用flush岁诉,依次將事件傳遞給InputDispatcher;
InputDispatcher在收到事件后跋选,會重新封裝為一個(gè)KeyEntry對象涕癣,并壓入mInboundQueue列表中;
最后喚醒InputDispatcherThread線程。
3.事件分發(fā)機(jī)制
??為了讓大家不會迷失在源碼中坠韩,筆者先拋出幾個(gè)問題距潘,然后帶著問題去看源碼。
- 問題1:窗口什么時(shí)候傳入IMS輸入系統(tǒng)的只搁?IMS是如何找到對應(yīng)的Window窗口的音比?
- 問題2:IMS和WMS是通過什么方式通訊將事件分發(fā)出去的?
??這里再提前放一張圖讓大家對輸入事件模型有個(gè)概念氢惋,然后再結(jié)合源碼去分析洞翩。
3.1 事件分發(fā)入口確定
??前面講到事件讀取流程將事件放入封裝為KeyEntry放入到mInboundQueue隊(duì)列的尾部tail,并喚醒InputDispatcherThread線程焰望,就以這里為入口骚亿。
void InputDispatcher::notifyKey(const NotifyKeyArgs* args) {
...
bool needWake;
{ // acquire lock
...
KeyEntry* newEntry = new KeyEntry(args->eventTime,
args->deviceId, args->source, policyFlags,
args->action, flags, keyCode, args->scanCode,
metaState, repeatCount, args->downTime);//1.封裝成KeyEntry
needWake = enqueueInboundEventLocked(newEntry); //2.將KeyEntry對象壓入mInboundQueue中
} // release lock
if (needWake) {
mLooper->wake();//3.喚醒InputDispatcherThread線程進(jìn)行處理
}
}
//---------------------------------------------
bool InputDispatcher::enqueueInboundEventLocked(EventEntry* entry) {
...
mInboundQueue.enqueueAtTail(entry);// 將entry加入到隊(duì)尾
}
3.2 事件分發(fā)線程喚醒
??進(jìn)入InputDispatcherThread的threadLoop方法: 關(guān)于Thread的threadLoop方法解釋已經(jīng)在捕捉輸入事件時(shí)講過,這里不再重復(fù)熊赖。
bool InputDispatcherThread::threadLoop() {
mDispatcher->dispatchOnce();//mDispatcher 是InputDispatcher類型對象
return true;
}
//-----------------------------------------
void InputDispatcher::dispatchOnce() {
...
if (!haveCommandsLocked()) {//1.判斷是否有指令需要執(zhí)行
dispatchOnceInnerLocked(&nextWakeupTime);//處理輸入事件
}
...
if (runCommandsLockedInterruptible()) {//2.執(zhí)行命令
nextWakeupTime = LONG_LONG_MIN;
}
mLooper->pollOnce(timeoutMillis);
}
注釋1處判斷是否有指令需要執(zhí)行来屠,如果沒有,則調(diào)用dispatchOnceInnerLocked去處理輸入事件震鹉,如果有俱笛,則優(yōu)先調(diào)用runCommandsLockedInterruptible處理事件, 為了讓線程可以立即進(jìn)入事件處理传趾,將nextWakeupTime 設(shè)置為LONG_LONG_MIN嫂粟,這樣線程在指令執(zhí)行完畢后可以立即被喚醒去處理輸入事件。
??從這里可以看出dispatchOnce主要是做了兩個(gè)功能:1.執(zhí)行指令 2.處理輸入事件墨缘,且指令執(zhí)行優(yōu)先級高于輸入事件處理星虹。 這里的指令例如:對waitQueue中的事件進(jìn)行出棧,后面會講到镊讼。
??進(jìn)入注釋2輸入事件處理:dispatchOnceInnerLocked
void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) {
switch (mPendingEvent->type) {
...
case EventEntry::TYPE_KEY: {// 按鍵事件類型
KeyEntry* typedEntry = static_cast<KeyEntry*>(mPendingEvent);
...
done = dispatchKeyLocked(currentTime, typedEntry, &dropReason, nextWakeupTime);
break;
}
case EventEntry::TYPE_MOTION: {// 移動事件類型
MotionEntry* typedEntry = static_cast<MotionEntry*>(mPendingEvent);
...
done = dispatchMotionLocked(currentTime, typedEntry,
&dropReason, nextWakeupTime);
break;
}
if (done) {
...
releasePendingEventLocked(); // 事件處理分發(fā)完畢后宽涌,用于釋放對應(yīng)的資源
*nextWakeupTime = LONG_LONG_MIN; // force next poll to wake up immediately
}
}
dispatchOnceInnerLocked方法主要是根據(jù)事件的類型調(diào)用不同的處理方法。 我們拿TYPE_KEY 按鍵事件來作為主線蝶棋,關(guān)于TYPE_MOTION觸摸事件處理邏輯都是差不過的卸亮,感興趣的同學(xué)可以自行閱讀源碼。 在事件處理分發(fā)完畢后會調(diào)用releasePendingEventLocked里面會釋放對應(yīng)的內(nèi)存資源
??mPendingEvent代表當(dāng)前需要處理的輸入事件,傳遞給dispatchKeyLocked去處理。
bool InputDispatcher::dispatchKeyLocked(nsecs_t currentTime, KeyEntry* entry,
DropReason* dropReason, nsecs_t* nextWakeupTime) {
...
Vector<InputTarget> inputTargets;
int32_t injectionResult = findFocusedWindowTargetsLocked(currentTime,
entry, inputTargets, nextWakeupTime); //1.找到焦點(diǎn)窗口
if (injectionResult == INPUT_EVENT_INJECTION_PENDING) {
return false;
}
...
addMonitoringTargetsLocked(inputTargets);//2.放入一個(gè)監(jiān)聽input通道
// Dispatch the key.
dispatchEventLocked(currentTime, entry, inputTargets); //3.實(shí)際處理事件 處
}
dispatchKeyLocked在注釋1處獲取按鍵事件對應(yīng)的Window窗口勘纯,在注釋2處放入一個(gè)監(jiān)聽input通道笔咽,注釋3處實(shí)際處理事件處。
??在講解注釋1處獲取窗口邏輯前我們先來看下我們窗口是如何傳入到InputDispatcher對象中的以及InputChannel概念糠排。
3.3 事件分發(fā)通道注冊
??我們Window是在ViewRootImpl的setView方法中傳入WMS的。
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
...
mInputChannel = new InputChannel();//創(chuàng)建通道
res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
getHostVisibility(), mDisplay.getDisplayId(),
mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
mAttachInfo.mOutsets, mInputChannel);
if (mInputChannel != null) {
mInputEventReceiver = new WindowInputEventReceiver(mInputChannel,Looper.myLooper());
}
}
mWindowSession是IWindowSession在app端的代理對象。實(shí)際執(zhí)行的是Session類:
//frameworks\base\services\core\java\com\android\server\wm\Session.java
public int addToDisplay(IWindow window,...InputChannel outInputChannel) {
return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId,
outContentInsets, outStableInsets, outOutsets, outInputChannel);
}
這里的mService是WMS對象螺垢,重點(diǎn)記住最后一個(gè)參數(shù)outInputChannel。
//WMS:
public int addWindow(Session session, IWindow client...InputChannel outInputChannel) {
...
final WindowState win = new WindowState(this, session, client, token, parentWindow,
appOp[0], seq, attrs, viewVisibility, session.mUid,
session.mCanAddInternalSystemWindow);
...
win.openInputChannel(outInputChannel); //1.打開InputChannel通道
if (focusChanged) {
mInputMonitor.setInputFocusLw(mCurrentFocus, false /*updateInputWindows*/);//2
}
}
addWindow的注釋1處調(diào)用WindowState打開InputChannel通道,什么是InputChannel通道呢枉圃?
??進(jìn)入WindowState的openInputChannel看看:
void openInputChannel(InputChannel outInputChannel) {
...
String name = getName();
InputChannel[] inputChannels = InputChannel.openInputChannelPair(name);//1
mInputChannel = inputChannels[0];//socket服務(wù)端的InputChannel
mClientChannel = inputChannels[1];//socket客戶端的InputChannel
mInputWindowHandle.inputChannel = inputChannels[0];
if (outInputChannel != null) {
mClientChannel.transferTo(outInputChannel);//將
mClientChannel.dispose();
mClientChannel = null;
}
...
mService.mInputManager.registerInputChannel(mInputChannel, mInputWindowHandle);//2
}
//--------------------------------------------------
//InputChannel.java:
public static InputChannel[] openInputChannelPair(String name) {
...
return nativeOpenInputChannelPair(name);
}
//--------------------------------------------------
//android_view_InputChannel.cpp:
static jobjectArray android_view_InputChannel_nativeOpenInputChannelPair(JNIEnv* env...) {
...
sp<InputChannel> serverChannel;
sp<InputChannel> clientChannel;
status_t result = InputChannel::openInputChannelPair(name, serverChannel, clientChannel);
jobjectArray channelPair = env->NewObjectArray(2, gInputChannelClassInfo.clazz, NULL);
jobject serverChannelObj = android_view_InputChannel_createInputChannel(env,
std::make_unique<NativeInputChannel>(serverChannel));
jobject clientChannelObj = android_view_InputChannel_createInputChannel(env,
std::make_unique<NativeInputChannel>(clientChannel));
...
env->SetObjectArrayElement(channelPair, 0, serverChannelObj);
env->SetObjectArrayElement(channelPair, 1, clientChannelObj);
return channelPair;
}
//--------------------------------------------------
status_t InputChannel::openInputChannelPair(const String8& name,
sp<InputChannel>& outServerChannel, sp<InputChannel>& outClientChannel) {
int sockets[2];
if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sockets)) {
...
return result;
}
...
outServerChannel = new InputChannel(serverChannelName, sockets[0]);
outClientChannel = new InputChannel(clientChannelName, sockets[1]);
return OK;
}
通過以上代碼可以看出InputChannel使用的是sockets通訊功茴,且WindowState的openInputChannel中注釋1處:InputChannel[] inputChannels = InputChannel.openInputChannelPair(name),返回的inputChannels是一個(gè)服務(wù)端和客戶端的輸入通道數(shù)組孽亲,其中: 下標(biāo)0:表示服務(wù)端的InputChannel;下標(biāo)1:表示客戶端的InputChannel坎穿。
??有了這些基礎(chǔ)我們再來細(xì)細(xì)品味下這段代碼:為什么registerInputChannel傳遞的是mInputChannel(socket服務(wù)端)而不是mClientChannel(socket客戶端)。
void openInputChannel(InputChannel outInputChannel) {
...
String name = getName();
InputChannel[] inputChannels = InputChannel.openInputChannelPair(name);//1
mInputChannel = inputChannels[0];//socket服務(wù)端的InputChannel
mClientChannel = inputChannels[1];//socket客戶端的InputChannel
mInputWindowHandle.inputChannel = inputChannels[0];
if (outInputChannel != null) {
// app的outInputChannel和socket的客戶端mClientChannel關(guān)聯(lián)
mClientChannel.transferTo(outInputChannel);//outInputChannel:app層傳遞進(jìn)來的InputChannel
mClientChannel.dispose();
mClientChannel = null;
}
...
mService.mInputManager.registerInputChannel(mInputChannel, mInputWindowHandle);//2
}
通過前面分析知:
mInputChannel:socket服務(wù)端的InputChannel返劲;
mClientChannel:socket客戶端的InputChannel玲昧;
outInputChannel:app層傳遞進(jìn)來的InputChannel。
3.3.1 InputChannel創(chuàng)建流程
- Android進(jìn)程間通信:LocalSocket
- 【framework】InputChannel創(chuàng)建流程
- AndroidR Input子系統(tǒng)(8)InputChannel注冊旭等,建立APP和InputDispatcher的連接
- Input機(jī)制之-InputChannel建立連接
3.3.2 Channel模型
- 調(diào)用mService.mInputManager.registerInputChannel
??將wms在socket服務(wù)端的InputChannel(mInputChannel)注冊到IMS中酌呆。這樣在IMS輸入系統(tǒng)就可以給socket服務(wù)端的InputChannel(mInputChannel)寫入數(shù)據(jù),在WMS的socket客戶端InputChannel(mClientChannel)就可以讀取數(shù)據(jù)搔耕。
- 調(diào)用mClientChannel.transferTo(outInputChannel)
??將app端的InputChannel(outInputChannel)和wms的socket客戶端InputChannel(mClientChannel)關(guān)聯(lián)隙袁,這樣就可以向socket客戶端InputChannel(mClientChannel)中寫入數(shù)據(jù)然后通知app端的InputChannel(outInputChannel),實(shí)際傳遞給ViewRootImpl對象處理弃榨,接著就是View層面的處理了菩收。
??這里我們繼續(xù)看注釋2處:mService.mInputManager.registerInputChannel 最終會進(jìn)入native層:
//frameworks\base\services\core\jni\com_android_server_input_InputManagerService.cpp
static void nativeRegisterInputChannel(JNIEnv* env, jclass /* clazz */,
jlong ptr, jobject inputChannelObj, jobject inputWindowHandleObj, jboolean monitor) {
NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
...
status_t status = im->registerInputChannel(
env, inputChannel, inputWindowHandle, monitor);//1.socket的服務(wù)端
}
frameworks/base/services/core/jni/com_android_server_input_InputManagerService.cpp
status_t NativeInputManager::registerInputChannel(JNIEnv* /* env */,
const sp<InputChannel>& inputChannel,
const sp<InputWindowHandle>& inputWindowHandle, bool monitor) {
return mInputManager->getDispatcher()->registerInputChannel(
inputChannel, inputWindowHandle, monitor);
}
//----------------------------------------------
//frameworks/native/services/inputflinger/InputDispatcher.cpp
status_t InputDispatcher::registerInputChannel(const sp<InputChannel>& inputChannel,
const sp<InputWindowHandle>& inputWindowHandle, bool monitor) {
...
sp<Connection> connection = new Connection(inputChannel, inputWindowHandle, monitor);
int fd = inputChannel->getFd();
mConnectionsByFd.add(fd, connection);
...
// registerInputChannel里面?zhèn)魅氲膍onitor是false --> nativeRegisterInputChannel(mPtr, inputChannel, inputWindowHandle, false);
// 所以這個(gè)流程不會將窗口的channel放到mMonitoringChannels里面
if (monitor) {
mMonitoringChannels.push(inputChannel);
}
...
}
im->registerInputChannel參數(shù)說明:
inputChannel:WMS在socket服務(wù)端InputChannel(mInputChannel);
inputWindowHandle:WMS內(nèi)的一個(gè)包含Window所有信息的實(shí)例鲸睛;
monitor:值為false娜饵,表示不加入監(jiān)控。
??InputChannel時(shí)序圖如下:
??最后階段在InputDispatcher中創(chuàng)建一個(gè)Connection并加入到mConnectionsByFd隊(duì)列中官辈,key為當(dāng)前inputChannel的fd箱舞。獲取的時(shí)候也是通過inputChannel的fd去獲取。
通過上面的分析我們知道了拳亿,和輸入系統(tǒng)InputDispatcher使用的socket通訊晴股,在View端,WMS端以及IMS端都有一個(gè)InputChannel肺魁。
View端:outInputChannel(和mClientChannel綁定一起)电湘;
WMS端:mClientChannel(socket客戶端);
IMS端:mInputChannel(socket服務(wù)端)鹅经。
InputDispatcher -> mInputChannel -> mClientChannel -> outInputChannel -> view
??哪個(gè)部分有數(shù)據(jù)需要分發(fā)就是將數(shù)據(jù)寫入通道中寂呛。 那么接下來我們看看IMS是如何選取對應(yīng)的通道的。
3.4 事件分發(fā)窗口確認(rèn)
??回到InputDispatcher::dispatchKeyLocked方法瘾晃。
bool InputDispatcher::dispatchKeyLocked(nsecs_t currentTime, KeyEntry* entry,
DropReason* dropReason, nsecs_t* nextWakeupTime) {
..
int32_t injectionResult = findFocusedWindowTargetsLocked(currentTime,
entry, inputTargets, nextWakeupTime); //1.找到焦點(diǎn)窗口
dispatchEventLocked(currentTime, entry, inputTargets); //2.處理分發(fā)事件
}
進(jìn)入findFocusedWindowTargetsLocked:這個(gè)方法就是用來確認(rèn)當(dāng)前需要傳遞事件的窗口贷痪。 重點(diǎn)看inputTargets的賦值操作。
int32_t InputDispatcher::findFocusedWindowTargetsLocked(...inputTargets){
...
addWindowTargetLocked(mFocusedWindowHandle,
InputTarget::FLAG_FOREGROUND | InputTarget::FLAG_DISPATCH_AS_IS, BitSet32(0),
inputTargets);
...
}
//----------------------------------------------
void InputDispatcher::addWindowTargetLocked(const sp<InputWindowHandle>& windowHandle,
int32_t targetFlags, BitSet32 pointerIds, Vector<InputTarget>& inputTargets) {
inputTargets.push();
const InputWindowInfo* windowInfo = windowHandle->getInfo();
InputTarget& target = inputTargets.editTop();
target.inputChannel = windowInfo->inputChannel;
target.flags = targetFlags;
target.xOffset = - windowInfo->frameLeft;
target.yOffset = - windowInfo->frameTop;
target.scaleFactor = windowInfo->scaleFactor;
target.pointerIds = pointerIds;
}
這里可以看出findFocusedWindowTargetsLocked方法中對inputTargets的頭部數(shù)據(jù)進(jìn)行了賦值酗捌,其中將windowInfo->inputChannel通道賦值給了target.inputChannel呢诬。 那么這個(gè)windowInfo是個(gè)什么涌哲?怎么獲扰昼汀尚镰?
??windowInfo是InputWindowHandle的屬性,而InputWindowHandle傳入的是一個(gè)mFocusedWindowHandle對象哪廓。 從名字也可以大概看出這是一個(gè)包含焦點(diǎn)Window信息的對象狗唉。
??那么這個(gè)焦點(diǎn)Window是在哪里賦值的呢?
3.5 事件分發(fā)窗口注冊
??我們回到WMS的addWindow步驟涡真。
// frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java
@Override
public int addWindow(Session session, IWindow client, int seq,
WindowManager.LayoutParams attrs, int viewVisibility, int displayId,
Rect outContentInsets, Rect outStableInsets, Rect outOutsets,
InputChannel outInputChannel) {
...
if (focusChanged) {
mInputMonitor.setInputFocusLw(mCurrentFocus, false /*updateInputWindows*/);
}
mInputMonitor.updateInputWindowsLw(false /*force*/);
...
}
在焦點(diǎn)發(fā)生改變的時(shí)候會調(diào)用setInputFocusLw方法和updateInputWindowsLw分俯,updateInputWindowsLw經(jīng)過層層調(diào)用最終會走到InputDispatcher::setInputWindows中。
// frameworks/native/services/inputflinger/InputDispatcher.cpp
void InputDispatcher::setInputWindows(const Vector<sp<InputWindowHandle> >& inputWindowHandles) {
...
mWindowHandles = inputWindowHandles;
sp<InputWindowHandle> newFocusedWindowHandle;
...
for (size_t i = 0; i < mWindowHandles.size(); i++) {
const sp<InputWindowHandle>& windowHandle = mWindowHandles.itemAt(i);
...
if (windowHandle->getInfo()->hasFocus) {
newFocusedWindowHandle = windowHandle;
}
...
mFocusedWindowHandle = newFocusedWindowHandle;
}
...
}
看到了這里對mWindowHandles和mFocusedWindowHandle做了賦值哆料。
- mWindowHandles:代表所有Window的Handler對象缸剪;
- mFocusedWindowHandle:表示焦點(diǎn)Window的Handler對象,通過這些代碼就讓我們IMS中獲取到了需要處理的焦點(diǎn)Window东亦。
??window窗口賦值時(shí)序圖如下:
3.6 事件分發(fā)最終處理
??繼續(xù)回到回到InputDispatcher::dispatchKeyLocked方法的注釋2杏节。
dispatchEventLocked(currentTime, entry, inputTargets); //2
void InputDispatcher::dispatchEventLocked(nsecs_t currentTime,
EventEntry* eventEntry, const Vector<InputTarget>& inputTargets) {
...
for (size_t i = 0; i < inputTargets.size(); i++) {
const InputTarget& inputTarget = inputTargets.itemAt(i);
ssize_t connectionIndex = getConnectionIndexLocked(inputTarget.inputChannel);
if (connectionIndex >= 0) {
sp<Connection> connection = mConnectionsByFd.valueAt(connectionIndex);
prepareDispatchCycleLocked(currentTime, connection, eventEntry, &inputTarget);
}
...
}
}
//-----------------------------------------------------
ssize_t InputDispatcher::getConnectionIndexLocked(const sp<InputChannel>& inputChannel) {
ssize_t connectionIndex = mConnectionsByFd.indexOfKey(inputChannel->getFd());
if (connectionIndex >= 0) {
sp<Connection> connection = mConnectionsByFd.valueAt(connectionIndex);
if (connection->inputChannel.get() == inputChannel.get()) {
return connectionIndex;
}
}
return -1;
}
dispatchEventLocked主要作用:輪詢inputTargets,根據(jù)inputTarget.inputChannel獲取其在mConnectionsByFd中的索引典阵,根據(jù)索引獲取Connection對象奋渔,并調(diào)用prepareDispatchCycleLocked進(jìn)行處理。
??prepareDispatchCycleLocked方法內(nèi)部調(diào)用了enqueueDispatchEntriesLocked方法:
void InputDispatcher::enqueueDispatchEntriesLocked(connection,..){
// Enqueue dispatch entries for the requested modes.
enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,...);//1
...
// If the outbound queue was previously empty, start the dispatch cycle going.
if (wasEmpty && !connection->outboundQueue.isEmpty()) {//2
startDispatchCycleLocked(currentTime, connection);//3
}
}
//--------------------------------------------
void InputDispatcher::enqueueDispatchEntryLocked(
const sp<Connection>& connection, EventEntry* eventEntry, const InputTarget* inputTarget,
int32_t dispatchMode) {
...
DispatchEntry* dispatchEntry = new DispatchEntry(eventEntry,
inputTargetFlags, inputTarget->xOffset, inputTarget->yOffset,
inputTarget->scaleFactor);
switch (eventEntry->type) {
case EventEntry::TYPE_KEY: {
KeyEntry* keyEntry = static_cast<KeyEntry*>(eventEntry);
dispatchEntry->resolvedAction = keyEntry->action;
dispatchEntry->resolvedFlags = keyEntry->flags;
...
break;
}
...
}
...
connection->outboundQueue.enqueueAtTail(dispatchEntry);
...
}
在注釋1處enqueueDispatchEntryLocked方法中會將輸入事件重新封裝為一個(gè)DispatchEntry并壓入connection的outboundQueue隊(duì)列中壮啊。 然后在注釋2處判斷如果事件不為空嫉鲸,則調(diào)用startDispatchCycleLocked循環(huán)發(fā)送輸入事件。
void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime,
const sp<Connection>& connection) {
while (connection->status == Connection::STATUS_NORMAL
&& !connection->outboundQueue.isEmpty()) {
DispatchEntry* dispatchEntry = connection->outboundQueue.head;
...
EventEntry* eventEntry = dispatchEntry->eventEntry;
switch (eventEntry->type) {
case EventEntry::TYPE_KEY: {
KeyEntry* keyEntry = static_cast<KeyEntry*>(eventEntry);
// Publish the key event.
status = connection->inputPublisher.publishKeyEvent(dispatchEntry->seq,
keyEntry->deviceId, keyEntry->source,
dispatchEntry->resolvedAction, dispatchEntry->resolvedFlags,
keyEntry->keyCode, keyEntry->scanCode,
keyEntry->metaState, keyEntry->repeatCount, keyEntry->downTime,
keyEntry->eventTime);
break;
}
...
connection->outboundQueue.dequeue(dispatchEntry);
connection->waitQueue.enqueueAtTail(dispatchEntry)
}
...
}
startDispatchCycleLocked方法中調(diào)用publishKeyEvent歹啼,其內(nèi)部會將事件寫入到WMS傳遞下來的InputChannel通道中玄渗。這樣WMS端的InputChannel就可以通過socket獲取到事件信息。在發(fā)送完畢后會將事件移出connection->outboundQueue隊(duì)列狸眼,并放入到waitQueue等待隊(duì)列中藤树,等待事件處理完畢后再移出。
??waitQueue用來監(jiān)聽當(dāng)前分發(fā)給WMS的輸入事件是否已經(jīng)被處理完畢份企。什么時(shí)候知道事件處理完畢呢也榄?
??在InputDispatcher::registerInputChannel方法里面注冊了handleReceiveCallback回調(diào):
status_t InputDispatcher::registerInputChannel(...) {
...
mLooper->addFd(fd, 0, ALOOPER_EVENT_INPUT, handleReceiveCallback, this);
...
}
當(dāng)app層的事件處理完畢之后就會回調(diào)handleReceiveCallback:
int InputDispatcher::handleReceiveCallback(int fd, int events, void* data) {
InputDispatcher* d = static_cast<InputDispatcher*>(data);
...
d->finishDispatchCycleLocked(currentTime, connection, seq, handled);
...
d->runCommandsLockedInterruptible();
...
}
這里會先調(diào)用InputDispatcher::finishDispatchCycleLocked去往mCommandQueue里面加入一個(gè)執(zhí)行InputDispatcher:: doDispatchCycleFinishedLockedInterruptible的Command:
void InputDispatcher::doDispatchCycleFinishedLockedInterruptible(
CommandEntry* commandEntry) {
sp<Connection> connection = commandEntry->connection;
...
DispatchEntry* dispatchEntry = connection->findWaitQueueEntry(seq);
...
if (dispatchEntry == connection->findWaitQueueEntry(seq)) {
connection->waitQueue.dequeue(dispatchEntry);
...
}
}
doDispatchCycleFinishedLockedInterruptible中會將connection->waitQueue出棧,這樣整個(gè)輸入系統(tǒng)的分發(fā)過程就閉環(huán)了司志。
3.7 事件分發(fā)流程小結(jié)
ViewRootImpl在setView方法會創(chuàng)建一個(gè)InputChannel通道甜紫,并在將Window添加給WMS的過程時(shí),以參數(shù)傳遞給WMS骂远。
WMS在添加Window的過程中會調(diào)用updateInputWindows囚霸,這個(gè)方法最終會調(diào)用到InputDispatcher::setInputWindows中, 并給InputDispatcher的Window隊(duì)列以及焦點(diǎn)Window賦值激才,這樣IMS就可以找到對應(yīng)的Window了拓型。
在WMS在添加Window的過程中還會創(chuàng)建一個(gè)socketpair通道的InputChannel额嘿,其中客戶端的socket與app層的InputChannel關(guān)聯(lián),用于WMS與app通訊劣挫;服務(wù)端的socket傳遞給IMS册养,用于IMS和WMS通訊。
客戶端在接收到輸入事件后压固,會根據(jù)當(dāng)前焦點(diǎn)Window的的InputChannel找到對應(yīng)的Connection連接球拦,這個(gè)Connection用于與WMS進(jìn)行通訊,內(nèi)部其實(shí)就是使用前面的socket通訊帐我。
事件分發(fā)后將輸入事件放入到waitQueue中坎炼,等待事件處理完畢后,將事件移出waitQueue
3.8 問題復(fù)盤
??那么關(guān)于開頭的兩個(gè)問題:
- 問題1:窗口什么時(shí)候傳入IMS輸入系統(tǒng)的拦键?
??IMS又是如何找到對應(yīng)的Window窗口的谣光? ViewRootImpl在setView方法中,調(diào)用addToDisplay將Window傳遞給WMS的時(shí)候芬为,這個(gè)時(shí)候會調(diào)用InputMonitor.updateInputWindowsLw方法萄金,最終會調(diào)用到InputDispatcher::setInputWindows,這里面會對IMS的Window屬性進(jìn)行賦值碳柱。IMS根據(jù)前面賦值的Window屬性捡絮,就可以找到對應(yīng)的焦點(diǎn)Window。
- 問題2:IMS和WMS是通過什么方式通訊將事件分發(fā)出去的莲镣?
??IMS和WMS是通過InputChannel通道進(jìn)行通訊的福稳,WMS在Window添加過程中會創(chuàng)建一個(gè)socket通道,將server端通道傳遞給IMS瑞侮,而client端通道用于WMS中接收server端事件的圆,server端根據(jù)對應(yīng)的Window,找到對應(yīng)的Connection半火,然后使用Connection進(jìn)行通訊越妈,而Connection內(nèi)部就是通過socket進(jìn)行通訊的。