上一个blog已经重点讲解了app层面自己产生的Cancel触摸事件,大概产生的原理如下:
上一个blog地址:https://blog.csdn.net/learnframework/article/details/124086882
即可以看出来,在服务端systemserver其实传递的触摸事件依然是move,只是move事件到了app端后,由于app端自己的业务把这个传递的move事件变成的cancel
视频讲解:https://www.bilibili.com/video/BV1nY4y1e713/
那么疑问来了?这个有没有存在systemserver传递事件时候就是已经变成cancel了呢?
如下图:
答案当然是有的。下面就进行详细分析
复现场景:
1、手机设置成导航按键模式桌面点击
2 、点击一个应用进入,然后手指一直触摸再应用内
3、然后另一个手点击导航键home按键,让回到桌面
以上3步即可以复现点击进去的应用内接受到一个Cancel事件,因为手其实一直触摸在屏幕,所以当然不存在接受到up,但是毕竟这个时候应用已经被退到后台,所以就只能给一个cancel事件给应用。这个cancel事件就是systemserver中inputdispatcher传递给应用的。
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、复现时候查看日志
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”
可以根据这个reason来追一下相关代码:
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相关信息变成最新
updateWindowHandlesForDisplayLocked(windowInfoHandles, displayId);
//最为关键的mTouchStatesByDisplay变量,一般保存就是当前触摸事件的派发情况,主要保存了派发触摸相关的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) {
//开始触发相关的cancel事件
CancelationOptions options(CancelationOptions::CANCEL_POINTER_EVENTS,
"touched window was removed");
synthesizeCancelationEventsForInputChannelLocked(touchedInputChannel, options);
}
}
state.windows.erase(state.windows.begin() + i);
} else {
++i;
}
}
//省略
}
setInputWindowsLocked主要是在系统有窗口window更新时候才会触发调用,比如我们上面演示场景的,按home按键后应用画面要退出后台,这个时候肯定应用的window就没有了,就会触发改方法。
1、updateWindowHandlesForDisplayLocked
这里会把最新的window信息更新到inputdispatcher的mWindowHandlesByDisplay变量中
2、方法内主要变量有一个mTouchStatesByDisplay:
最为关键的mTouchStatesByDisplay变量,一般保存就是当前触摸事件的派发情况,主要保存了派发触摸相关的window信息
即代表当前的触摸事件派发相关window的的记录
3、还有另一个关键方法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、找到对应删除的window的inputchannel,传递对应的cancel事件
//获取touchedInputChannel
std::shared_ptr touchedInputChannel =
getInputChannelLocked(touchedWindow.windowHandle->getToken());
//派发cancel事件到touchedInputChannel
synthesizeCancelationEventsForInputChannelLocked(touchedInputChannel, options);
如果发现更新之后的window的中已经没有了正在派发事件的window,那么说明window已经被移除,然后就会触发相关的cancel事件到原来的window。
最后更多干货直接找千里马可以+w ; androidframework007