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

相关推荐
whysqwhw13 分钟前
OkHttp PublicSuffix包的平台化设计分析
android
whysqwhw13 分钟前
Conscrypt 源码分析全图解(附精要讲解)
android
一只柠檬新22 分钟前
Kotlin object单例到底是懒汉式还是饿汉式
android·kotlin
wkj0011 小时前
php中array($this, ‘loadClass‘)表示啥意思?
android·开发语言·php
呼啸长风2 小时前
FastKV的轻量化回归
android·开源·github
半桔2 小时前
【Linux手册】从接口到管理:Linux文件系统的核心操作指南
android·java·linux·开发语言·面试·系统架构
xiaopengbc3 小时前
Android解压工具,ZArchiver,RAR for Android,iZip,The Unarchiver,解压专家
android
Digitally3 小时前
5种使用USB数据线将文件从安卓设备传输到电脑的方法
android
爬虫程序猿10 小时前
利用爬虫按关键字搜索淘宝商品实战指南
android·爬虫
顾北川_野11 小时前
Android ttyS2无法打开该如何配置 + ttyS0和ttyS1可以
android·fpga开发