• 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

    /**
     * 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);
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    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.
    
    
    • 1
    • 2

    这里即可以看出有同步一个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");
    
    
    • 1
    • 2
    • 3

    找到了在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;
                }
            }
    //省略
    }
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42

    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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    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

  • 相关阅读:
    远超预期,特效吹爆!《武庚纪》:建议漫改都按这个标准来!
    知识产权之围:跨境电商卖家的法律防线
    Java开发注意事项和细节说明
    centos7下 编译coreboot生成真机可用的bios固件, 并在真机上演示 (上篇)
    JAVA毕业设计科普网站计算机源码+lw文档+系统+调试部署+数据库
    第六章 图(中)【图的基本操作和遍历】
    windows无法使用hadoop报错:系统找不到路径
    TensorFlow(1):深度学习的介绍
    2024 前端面试每日1小时
    【游戏建模全流程】使用Maya制作赛车模型
  • 原文地址:https://blog.csdn.net/learnframework/article/details/132797212