systemserver的inputdispatcher直接产生CANCEL事件原理分析-讨厌的android触摸面试题

背景回顾:

上一个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了呢?

如下图:

答案当然是有的。下面就进行详细分析

2、systemserver端变成cancel事件

复现场景:

1、手机设置成导航按键模式桌面点击

2 、点击一个应用进入,然后手指一直触摸再应用内

3、然后另一个手点击导航键home按键,让回到桌面

以上3步即可以复现点击进去的应用内接受到一个Cancel事件,因为手其实一直触摸在屏幕,所以当然不存在接受到up,但是毕竟这个时候应用已经被退到后台,所以就只能给一个cancel事件给应用。这个cancel事件就是systemserver中inputdispatcher传递给应用的。

下面来进行源码分析cancel事件在inputdispatcher产生

1、开启日志

开放DEBUG_OUTBOUND_EVENT_DETAILS日志,这里可以用adb 命令也可以直接修改变成true

cpp 复制代码
/**
 * 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、复现时候查看日志

bash 复制代码
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来追一下相关代码:

bash 复制代码
test@test:~/nx563j_xiaomi/frameworks/native$ grep "touched window was removed" ./ -rn
./services/inputflinger/dispatcher/InputDispatcher.cpp:4759:                                               "touched window was removed");

找到了在InputDispatcher的4759行:

cpp 复制代码
/**
 * 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

cpp 复制代码
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

相关推荐
潜龙952711 小时前
第3.2.3节 Android动态调用链路的获取
android·调用链路
追随远方12 小时前
Android平台FFmpeg音视频开发深度指南
android·ffmpeg·音视频
撰卢13 小时前
MySQL 1366 - Incorrect string value:错误
android·数据库·mysql
恋猫de小郭14 小时前
Flutter 合并 ‘dot-shorthands‘ 语法糖,Dart 开始支持交叉编译
android·flutter·ios
牛马程序小猿猴14 小时前
15.thinkphp的上传功能
android
林家凌宇14 小时前
Flutter 3.29.3 花屏问题记录
android·flutter·skia
时丶光15 小时前
Android 查看 Logcat (可纯手机方式 无需电脑)
android·logcat
血手人屠喵帕斯15 小时前
事务连接池
android·adb
恋猫de小郭16 小时前
React Native 前瞻式重大更新 Skia & WebGPU & ThreeJS,未来可期
android·javascript·flutter·react native·react.js·ios
一人一萧十只猫�16 小时前
MySQL 从入门到精通(三):日志管理详解 —— 从排错到恢复的核心利器
android·mysql·adb