systemserver的inputdispatcher直接產(chǎn)生CANCEL事件原理分析-討厭的android觸摸面試題
背景回顧:
上一個blog已經(jīng)重點講解了app層面自己產(chǎn)生的Cancel觸摸事件,大概產(chǎn)生的原理如下:

即可以看出來,再服務(wù)端systemserver其實傳遞的觸摸事件依然是move,只是move事件到了app端后,由于app端自己的業(yè)務(wù)把這個傳遞的move事件變成的cancel視頻講解:https://www.bilibili.com/video/BV1nY4y1e713/[1]

那么疑問來了?這個又沒有存在systemserver傳遞事件時候就是已經(jīng)變成呢搞cancel了呢?如下圖:

答案當(dāng)然是有的。下面就進行詳細(xì)分析
2、systemserver端變成cancel事件
復(fù)現(xiàn)場景: 1、手機設(shè)置成導(dǎo)航按鍵模式桌面點擊

2 、點擊一個應(yīng)用進入,然后手指一直觸摸再應(yīng)用內(nèi) 3、然后另一個手點擊導(dǎo)航鍵home按鍵,讓會到桌面
以上3步即可以雜唉點擊進去的應(yīng)用內(nèi)接受到一個Cancel事件,因為手其實一直觸摸在屏幕,所以當(dāng)然不存在接受到up,但是畢竟這個時候應(yīng)用已經(jīng)被退到后臺,所以就只能給一個cancel事件給應(yīng)用。這個cancel事件就是systemserver中inputdispatcher傳遞給應(yīng)用的。
下面來進行源碼分析cancel事件在inputdispatcher產(chǎn)生
1、開啟日志 開放DEBUG_OUTBOUND_EVENT_DETAILS日志,這里可以用adb 命令也可以直接修改變成true
/**
?*?Log?detailed?debug?messages?about?each?outbound?event?processed?by?the?dispatcher.
?*?Enable?this?via?"adb?shell?setprop?log.tag.InputDispatcherOutboundEvent?DEBUG"?(requires?restart)
?*/
const?bool?DEBUG_OUTBOUND_EVENT_DETAILS?=
???????true;//?__android_log_is_loggable(ANDROID_LOG_DEBUG,?LOG_TAG?"OutboundEvent",?ANDROID_LOG_INFO);
2、復(fù)現(xiàn)時候查看日志
09-10?22:49:50.775??2231??2357?D?InputDispatcher:?channel?'a1b72df?com.android.messaging/com.android.messaging.ui.conversationlist.ConversationListActivity?(server)'?~?Synthesized?1?cancelation?events?to?bring?channel?back?in?sync?with?reality:?touched?window?was?removed,?mode=1.
這里即可以看出有同步一個cancel事件給com.android.messaging/com.android.messaging.ui.conversationlist.ConversationListActivity,大家注意這個原因是“touched window was removed”
可以根據(jù)這個reason來追一下相關(guān)代碼:
test@test:~/nx563j_xiaomi/frameworks/native$?grep?"touched?window?was?removed"?./?-rn
./services/inputflinger/dispatcher/InputDispatcher.cpp:4759:???????????????????????????????????????????????"touched?window?was?removed");
找到了在InputDispatcher的4759行:
/**
?*?Called?from?InputManagerService,?update?window?handle?list?by?displayId?that?can?receive?input.
?*?A?window?handle?contains?information?about?InputChannel,?Touch?Region,?Types,?Focused,...
?*?If?set?an?empty?list,?remove?all?handles?from?the?specific?display.
?*?For?focused?handle,?check?if?need?to?change?and?send?a?cancel?event?to?previous?one.
?*?For?removed?handle,?check?if?need?to?send?a?cancel?event?if?already?in?touch.
?*/
void?InputDispatcher::setInputWindowsLocked(
????????const?std::vector<sp<WindowInfoHandle>>&?windowInfoHandles,?int32_t?displayId)?{
???//省略部分
???//把inputdispatcher的window相關(guān)信息變成最新
???????updateWindowHandlesForDisplayLocked(windowInfoHandles,?displayId);
???//最為關(guān)鍵的mTouchStatesByDisplay變量,一般保存就是當(dāng)前觸摸事件的派發(fā)情況,主要保存了派發(fā)觸摸相關(guān)的window信息
????std::unordered_map<int32_t,?TouchState>::iterator?stateIt?=
????????????mTouchStatesByDisplay.find(displayId);
????if?(stateIt?!=?mTouchStatesByDisplay.end())?{
????????TouchState&?state?=?stateIt->second;
????????for?(size_t?i?=?0;?i?<?state.windows.size();)?{
????????????TouchedWindow&?touchedWindow?=?state.windows[i];
????????????//拿正在觸摸的window信息與最新的window的信息比較看看是否還存在,如果不在說明消失了
????????????if?(getWindowHandleLocked(touchedWindow.windowHandle)?==?nullptr)?{
??????????????
????????????????std::shared_ptr<InputChannel>?touchedInputChannel?=
????????????????????????getInputChannelLocked(touchedWindow.windowHandle->getToken());
????????????????if?(touchedInputChannel?!=?nullptr)?{
????????????????//開始觸發(fā)相關(guān)的cancel事件
????????????????????CancelationOptions?options(CancelationOptions::CANCEL_POINTER_EVENTS,
???????????????????????????????????????????????"touched?window?was?removed");
????????????????????synthesizeCancelationEventsForInputChannelLocked(touchedInputChannel,?options);
???????????????
????????????????????}
????????????????}
????????????????state.windows.erase(state.windows.begin()?+?i);
????????????}?else?{
????????????????++i;
????????????}
????????}
//省略
}
setInputWindowsLocked主要是在系統(tǒng)有窗口window更新時候才會觸發(fā)調(diào)用,比如我們上面演示場景的,按home按鍵后應(yīng)用畫面要退出后臺,這個時候肯定應(yīng)用的window就沒有了,就會觸發(fā)改方法。 1、updateWindowHandlesForDisplayLocked 這里會把最新的window信息更新到inputdispatcher的mWindowHandlesByDisplay變量中 2、方法內(nèi)主要變量有一個mTouchStatesByDisplay:
最為關(guān)鍵的mTouchStatesByDisplay變量,一般保存就是當(dāng)前觸摸事件的派發(fā)情況,主要保存了派發(fā)觸摸相關(guān)的window信息
即代表當(dāng)前的觸摸事件派發(fā)相關(guān)window的的記錄
3、還有另一個關(guān)鍵方法getWindowHandleLocked
sp<WindowInfoHandle>?InputDispatcher::getWindowHandleLocked(
????????const?sp<IBinder>&?windowHandleToken)?const?{
????if?(windowHandleToken?==?nullptr)?{
????????return?nullptr;
????}
????????//就是拿傳入的windowHandleToken去mWindowHandlesByDisplay遍歷看看是否有
????for?(auto&?it?:?mWindowHandlesByDisplay)?{
????????const?std::vector<sp<WindowInfoHandle>>&?windowHandles?=?it.second;
????????for?(const?sp<WindowInfoHandle>&?windowHandle?:?windowHandles)?{
????????????if?(windowHandle->getToken()?==?windowHandleToken)?{
????????????????return?windowHandle;
????????????}
????????}
????}
????return?nullptr;
}
4、找到對應(yīng)刪除的window的inputchannel,傳遞對應(yīng)的cancel事件 //獲取touchedInputChannel ? ? ? std::shared_ptr touchedInputChannel = ? ? ? ? ? ? ? ? ? ? ? ?getInputChannelLocked(touchedWindow.windowHandle->getToken()); ? ? ? ? //派發(fā)cancel事件到touchedInputChannel ? ? ? ? ? ? ? ? ? ?synthesizeCancelationEventsForInputChannelLocked(touchedInputChannel, options);
如果發(fā)現(xiàn)更新之后的window的中已經(jīng)沒有了正在派發(fā)事件的window,那么說明window已經(jīng)被移除,然后就會觸發(fā)相關(guān)的cancel事件到原來的window。
最后更多干貨直接找千里馬可以+w ; androidframework007
引用鏈接
[1]
視頻講解:https://www.bilibili.com/video/BV1nY4y1e713/: https://www.bilibili.com/video/BV1nY4y1e713/