日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 >

Android输入系统(三)InputReader的加工类型和InputDispatcher的分发过程

發布時間:2025/5/22 68 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Android输入系统(三)InputReader的加工类型和InputDispatcher的分发过程 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

關聯系列
解析WMS系列
深入理解JNI系列
輸入系統系列

前言

在上一篇文章中,我們學習了輸入事件的處理,輸入事件會交由InputDispatcher進行分發,那么InputDispatcher是如何進行分發的?這篇文章會給你答案。

1.InputReader的加工類型

在Android輸入系統(二)IMS的啟動過程和輸入事件的處理這篇文章中,我們知道InputReader會對原始輸入事件進行加工,如果事件的類型為按鍵類型的事件,就會調用如下一段代碼。 frameworks/native/services/inputflinger/InputDispatcher.cpp

void InputDispatcher::notifyKey(const NotifyKeyArgs* args) {...bool needWake;{ ...} // release lockif (needWake) {mLooper->wake();} } 復制代碼

InputDispatcher的notifyKey方法用于喚醒InputDispatcherThread,它的參數NotifyKeyArgs是InputReader對按鍵類型的事件加工后得到的。 frameworks/native/services/inputflinger/InputListener.h

struct NotifyKeyArgs : public NotifyArgs {nsecs_t eventTime;int32_t deviceId;uint32_t source;uint32_t policyFlags;int32_t action;int32_t flags;int32_t keyCode;int32_t scanCode;int32_t metaState;nsecs_t downTime;inline NotifyKeyArgs() { }NotifyKeyArgs(nsecs_t eventTime, int32_t deviceId, uint32_t source, uint32_t policyFlags,int32_t action, int32_t flags, int32_t keyCode, int32_t scanCode,int32_t metaState, nsecs_t downTime);NotifyKeyArgs(const NotifyKeyArgs& other);virtual ~NotifyKeyArgs() { }virtual void notify(const sp<InputListenerInterface>& listener) const; }; 復制代碼

可以看到,NotifyKeyArgs結構體繼承自NotifyArgs結構體,如下圖所示。

NotifyArgs有三個子類,分別是NotifyKeyArgs、NotifyMotionArgs和NotifySwichArgs,這說明InputReader對原始輸入事件加工后,最終會得出三種事件類型,分別是key事件、Motion事件和Swich事件,這些事件會交由InputDispatcher來進行分發,如下圖所示。

2.InputDispatcher的分發過程

不同的事件類型有著不同的分發過程,其中Swich事件的處理是沒有派發過程的,在InputDispatcher的notifySwitch函數中會將Swich事件交由InputDispatcherPolicy來處理。本系列文章一直講解key事件相關,這次換一下,以Motion事件的分發過程來進行舉例,對key事件分發事件有興趣的可以自行去看源碼,本質上都差不多。

2.1 喚醒InputDispatcherThread

InputDispatcher的notifyMotion函數用來喚醒InputDispatcherThread。 frameworks/native/services/inputflinger/InputDispatcher.cpp

void InputDispatcher::notifyMotion(const NotifyMotionArgs* args) { #if DEBUG_INBOUND_EVENT_DETAILS ... #endif//檢查Motion事件的參數是否有效if (!validateMotionEvent(args->action, args->actionButton,args->pointerCount, args->pointerProperties)) {//1return;}uint32_t policyFlags = args->policyFlags;policyFlags |= POLICY_FLAG_TRUSTED;mPolicy->interceptMotionBeforeQueueing(args->eventTime, /*byref*/ policyFlags);bool needWake;{ // acquire lockmLock.lock();//Motion事件是否需要交由InputFilter過濾if (shouldSendMotionToInputFilterLocked(args)) {//2mLock.unlock();MotionEvent event;//初始化MotionEvent,將NotifyMotionArgs中的參數信息賦值給MotionEvent中的參數event.initialize(args->deviceId, args->source, args->action, args->actionButton,args->flags, args->edgeFlags, args->metaState, args->buttonState,0, 0, args->xPrecision, args->yPrecision,args->downTime, args->eventTime,args->pointerCount, args->pointerProperties, args->pointerCoords);//表示已經過濾了policyFlags |= POLICY_FLAG_FILTERED;//開始過濾,如果返回值為false,就會直接return,這次事件不再進行分發if (!mPolicy->filterInputEvent(&event, policyFlags)) {//3return; // event was consumed by the filter}mLock.lock();}/*** 4 */MotionEntry* newEntry = new MotionEntry(args->eventTime,args->deviceId, args->source, policyFlags,args->action, args->actionButton, args->flags,args->metaState, args->buttonState,args->edgeFlags, args->xPrecision, args->yPrecision, args->downTime,args->displayId,args->pointerCount, args->pointerProperties, args->pointerCoords, 0, 0);needWake = enqueueInboundEventLocked(newEntry);//5mLock.unlock();} // release lockif (needWake) {mLooper->wake();//6} } 復制代碼

注釋1處用于檢查Motion事件的參數是否有效,其內部會檢查觸控點的數量pointerCount是否在合理范圍內(小于1或者大于16都是不合理的),以及觸控點的ID是否在合理范圍內(小于0或者大于31都是不合理的)。 注釋2處如果Motion事件需要交由InputFilter過濾,就會初始化MotionEvent,其作用就是用NotifyMotionArgs中的事件參數信息構造一個MotionEvent,接著MotionEven會交給注釋3處的方法進行過濾,如果返回值為false,這次Motion事件就會被忽略掉。 注釋4處,用NotifyMotionArgs中的事件參數信息構造一個MotionEntry對象。注釋5處將MotionEntry傳入到enqueueInboundEventLocked函數中,其內部會將MotionEntry添加到InputDispatcher的mInboundQueue隊列的末尾,并返回一個值needWake,代表InputDispatcherThread是否需要喚醒,如果需要喚醒就調用注釋6處的代碼來喚醒InputDispatcherThread。

2.2 InputDispatcher進行分發

InputDispatcherThread被喚醒后,會執行InputDispatcherThread的threadLoop函數: frameworks/native/services/inputflinger/InputDispatcher.cpp

bool InputDispatcherThread::threadLoop() {mDispatcher->dispatchOnce();return true; } 復制代碼

threadLoop函數中只調用了InputDispatcher的dispatchOnce函數: frameworks/native/services/inputflinger/InputDispatcher.cpp

void InputDispatcher::dispatchOnce() {nsecs_t nextWakeupTime = LONG_LONG_MAX;{ // acquire lockAutoMutex _l(mLock);mDispatcherIsAliveCondition.broadcast();if (!haveCommandsLocked()) {//1dispatchOnceInnerLocked(&nextWakeupTime);//2}if (runCommandsLockedInterruptible()) {nextWakeupTime = LONG_LONG_MIN;}} // release locknsecs_t currentTime = now();//3int timeoutMillis = toMillisecondTimeoutDelay(currentTime, nextWakeupTime);//4mLooper->pollOnce(timeoutMillis); } 復制代碼

注釋1處用于檢查InputDispatcher的緩存隊列中是否有等待處理的命令,如果沒有就會執行注釋2處的dispatchOnceInnerLocked函數,用來將輸入事件分發給合適的。注釋3處獲取當前的時間,結合注釋4處,得出InputDispatcherThread需要睡眠的時間為timeoutMillis。最后調用Looper的pollOnce函數使InputDispatcherThread進入睡眠狀態,并將它的最長的睡眠的時間設置為timeoutMillis。當有輸入事件產生時,InputReader就會將睡眠狀態的InputDispatcherThread 喚醒,InputDispatcher會重新開始分發輸入事件。查看注釋2處的dispatchOnceInnerLocked函數是如何進行事件分發的。 frameworks/native/services/inputflinger/InputDispatcher.cpp

void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) {...// 如果InputDispatcher被凍結,則不進行派發操作if (mDispatchFrozen) { #if DEBUG_FOCUSALOGD("Dispatch frozen. Waiting some more."); #endifreturn;}//如果isAppSwitchDue為true,說明沒有及時響應HOME鍵等操作bool isAppSwitchDue = mAppSwitchDueTime <= currentTime;//1if (mAppSwitchDueTime < *nextWakeupTime) {//2*nextWakeupTime = mAppSwitchDueTime;}//如果還沒有待分發的事件,去mInboundQueue中取出一個事件if (! mPendingEvent) {//如果mInboundQueue為空,并且沒有待分發的事件,就returnif (mInboundQueue.isEmpty()) {...if (!mPendingEvent) {return;}} else {//如果mInboundQueue不為空,取隊列頭部的EventEntry賦值給mPendingEvent mPendingEvent = mInboundQueue.dequeueAtHead();traceInboundQueueLengthLocked();}if (mPendingEvent->policyFlags & POLICY_FLAG_PASS_TO_USER) {pokeUserActivityLocked(mPendingEvent);}resetANRTimeoutsLocked();}ALOG_ASSERT(mPendingEvent != NULL);bool done = false;DropReason dropReason = DROP_REASON_NOT_DROPPED;//3...switch (mPendingEvent->type) {//4...case EventEntry::TYPE_MOTION: {MotionEntry* typedEntry = static_cast<MotionEntry*>(mPendingEvent);//如果沒有及時響應窗口切換操作if (dropReason == DROP_REASON_NOT_DROPPED && isAppSwitchDue) {dropReason = DROP_REASON_APP_SWITCH;}//事件過期if (dropReason == DROP_REASON_NOT_DROPPED&& isStaleEventLocked(currentTime, typedEntry)) {dropReason = DROP_REASON_STALE;}//阻礙其他窗口獲取事件if (dropReason == DROP_REASON_NOT_DROPPED && mNextUnblockedEvent) {dropReason = DROP_REASON_BLOCKED;}done = dispatchMotionLocked(currentTime, typedEntry,&dropReason, nextWakeupTime);//5break;}default:ALOG_ASSERT(false);break;}if (done) {if (dropReason != DROP_REASON_NOT_DROPPED) {dropInboundEventLocked(mPendingEvent, dropReason);}mLastDropReason = dropReason;//釋放本次事件處理的對象releasePendingEventLocked();//6//使得InputDispatcher能夠快速處理下一個分發事件*nextWakeupTime = LONG_LONG_MIN;//7 } 復制代碼

InputDispatcher的dispatchOnceInnerLocked函數的代碼比較長,這里截取了和Motion事件的分發相關的主要源碼。主要做了以下幾件事。

  • InputDispatcher的凍結處理 如果當前InputDispatcher被凍結,則不進行派發操作,InputDispatcher有三種狀態,分別是正常狀態、凍結狀態和禁用狀態,可以通過InputDispatcher的setInputDispatchMode函數來設置。
  • 窗口切換操作處理 注釋1處的mAppSwitchDueTime ,代表了App最近發生窗口切換操作時(比如按下Home鍵、掛斷電話),該操作事件最遲的分發時間。如果這個時候,mAppSwitchDueTime小于等于當前系統時間,說明沒有及時響應窗口切換操作,則isAppSwitchDue的值設置為true。 注釋2處,如果mAppSwitchDueTime小于nextWakeupTime(下一次InputDispatcherThread醒來的時間),就將mAppSwitchDueTime賦值給nextWakeupTime,這樣當InputDispatcher處理完分發事件后,會第一時間處理窗口切換操作。
  • 取出事件 如果沒有待分發的事件,就從mInboundQueue中取出一個事件,如果mInboundQueue為空,并且沒有待分發的事件,就return,如果mInboundQueue不為空,取隊列頭部的EventEntry賦值給mPendingEvent,mPendingEvent的類型為EventEntry對象指針。
  • 事件丟棄 注釋3處的dropReason代表了事件丟棄的原因,它的默認值為DROP_REASON_NOT_DROPPED,代表事件不被丟棄。 注釋4處根據mPendingEvent的type做區分處理,這里主要截取了對Motion類型的處理。經過過濾,會調用注釋5處的dispatchMotionLocked函數為這個事件尋找合適的窗口。
  • 后續處理 如果注釋5處的事件分發成功,則會在注釋6處調用releasePendingEventLocked函數,其內部會將mPendingEvent的值設置為Null,并將mPendingEvent指向的對象內存釋放掉。注釋7處將nextWakeupTime的值設置為LONG_LONG_MIN,這是為了讓InputDispatcher能夠快速處理下一個分發事件。
  • 后記

    本文講解了InputReader的加工類型和InputDispatcher的分發過程,由于文章篇幅的原因,InputDispatcher的分發過程還有一部分沒有講解,這一部分就是事件分發到目標窗口的過程,會在本系列的下一篇文章進行講解。


    分享大前端、Java、跨平臺等技術,關注職業發展和行業動態。

    總結

    以上是生活随笔為你收集整理的Android输入系统(三)InputReader的加工类型和InputDispatcher的分发过程的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。