ActivityRecordInputSink的触摸区域TouchRegion详细解读

Android四大组件之Activity(三)—— Activity启动中涉及到的重要的数据结构,ActivityRecord等 1、使用的是Android 7.1.2的源代码: https://pan.baidu.com/s/1XcVD68cC_2wKtm8jJkdNQA przv 2、感谢IT先森的系列博客: Android应用进程创建流程大揭秘 Android四大组件之bindService源码实现详解 Android四大组件之Activity启动流程源码实现详解概要 Activity启动流程(一)发起端进程请求启动目标Activity Activity启动流程(二)system_server进程处理启动Activity请 阅读详情
AI摘要

本文深入探讨了Android系统中ActivityRecordInputSink的TouchRegion为何被设置为远超正常范围的巨大数值。通过源码分析,揭示了这一现象背后的原理及实现机制。

背景

在学习SurfaceFlinger或者说input课程时候,经常发现每个Activity都会有如下这个ActivityRecordSink图层
比如SurfaceFlinger
在这里插入图片描述
在看看input

在这里插入图片描述
可以看到桌面Activity的窗口touchRegion=[0,0][1440,2960],,这个属于正常就是全屏Activity的大小
但是明显看到了ActivityRecordInputSink这个inputWindow的大小却是touchableRegion=[-14399,-29599][14400,29600]
明显是一个巨大的数字,应该是正常长宽的10倍,而且还支持负数。

那么恨这个巨大的TouchRegion到底是在哪里设置的呢???

源码追踪:

上面dumpsys input看到了TouchRegion一般情况下传递流程是:

WMS —> SurfaceFlinger ----> input

即正常情况下TouchRegion都是wms触摸在InputMonitor进行传递的,比如如下堆栈:
在这里插入图片描述

上面是针对普通的WindowState来说都是这样一个设置流程,最后的input相关信息都是上面流程设置到sf的。
但是ActivityRecordSink明显较为特殊,因为他不是个WindowState,所以这里看看他的源码:
frameworks/base/services/core/java/com/android/server/wm/ActivityRecordInputSink.java

    private SurfaceControl createSurface(SurfaceControl.Transaction t) {
        SurfaceControl surfaceControl = mActivityRecord.makeChildSurface(null)
                .setName(mName)
                .setHidden(false)
                .setCallsite("ActivityRecordInputSink.createSurface")
                .build();
        // Put layer below all siblings (and the parent surface too)
        t.setLayer(surfaceControl, Integer.MIN_VALUE);
        return surfaceControl;
    }

他只是在sf中创建的一个Layer而已,那么他的inputwindow信息呢?

    private InputWindowHandleWrapper getInputWindowHandleWrapper() {
        if (mInputWindowHandleWrapper == null) {
            mInputWindowHandleWrapper = new InputWindowHandleWrapper(createInputWindowHandle());
        }
        // Don't block touches from passing through to an activity below us in the same task, if
        // that activity is either from the same uid or if that activity has launched an activity
        // in our uid.
        final ActivityRecord activityBelowInTask = mActivityRecord.getTask() != null
                ? mActivityRecord.getTask().getActivityBelow(mActivityRecord) : null;
        final boolean allowPassthrough = activityBelowInTask != null && (
                activityBelowInTask.mAllowedTouchUid == mActivityRecord.getUid()
                        || activityBelowInTask.isUid(mActivityRecord.getUid()));
        if (allowPassthrough || !mIsCompatEnabled || mActivityRecord.isInTransition()) {
            // Set to non-touchable, so the touch events can pass through.
            mInputWindowHandleWrapper.setInputConfigMasked(InputConfig.NOT_TOUCHABLE,
                    InputConfig.NOT_TOUCHABLE);
        } else {
            // Set to touchable, so it can block by intercepting the touch events.
            mInputWindowHandleWrapper.setInputConfigMasked(0, InputConfig.NOT_TOUCHABLE);
        }
        mInputWindowHandleWrapper.setDisplayId(mActivityRecord.getDisplayId());
        return mInputWindowHandleWrapper;
    }

可以看到这里并没又看到有对这个InputWindowHandle对象进行TouchRegion的设置,但是大家注意点,有对replaceTouchableRegionWithCrop进行设置为true。

那这里没有设置这个TouchRegion,但是input中又有TouchRegion,那么难道是Sf或者input中设置的??

那么再从input入手试试:
在input和sf交互的接口中加入日志
在这里插入图片描述
查看Sink相关打印如下:

06-18 16:06:22.350 12660 14044 E InputDispatcher: name 8e3471b ActivityRecordInputSink com.android.messaging/.ui.conversationlist.ConversationListActivity  setInputWindowsLocked touchableRegion  
06-18 16:06:22.350 12660 14044 D Region  :   Region  (this=0x758b86dba8, count=1)
06-18 16:06:22.351 12660 14044 D Region  :     [-10799, -19199, 10800, 19200]

可以看出sf传递过来的Region就是放大很大的,那么下面主要精力就要放在sf部分。

frameworks/native/services/surfaceflinger/SurfaceFlinger.cpp


void SurfaceFlinger::buildWindowInfos(std::vector<WindowInfo>& outWindowInfos,
                                      std::vector<DisplayInfo>& outDisplayInfos) {
    static size_t sNumWindowInfos = 0;
    outWindowInfos.reserve(sNumWindowInfos);
    sNumWindowInfos = 0;

    if (mLayerLifecycleManagerEnabled) {
        mLayerSnapshotBuilder.forEachInputSnapshot(
                [&outWindowInfos](const frontend::LayerSnapshot& snapshot) {
                    outWindowInfos.push_back(snapshot.inputInfo);
                });
    } else {
        mDrawingState.traverseInReverseZOrder([&](Layer* layer) {
            if (!layer->needsInputInfo()) return;
            const auto opt =
                    mFrontEndDisplayInfos.get(layer->getLayerStack())
                            .transform([](const frontend::DisplayInfo& info) {
                                return Layer::InputDisplayArgs{&info.transform, info.isSecure};
                            });
				  //核心就是这里调用了layer的fillInputInfo
            outWindowInfos.push_back(layer->fillInputInfo(opt.value_or(Layer::InputDisplayArgs{})));
        });
    }

    sNumWindowInfos = outWindowInfos.size();

    outDisplayInfos.reserve(mFrontEndDisplayInfos.size());
    for (const auto& [_, info] : mFrontEndDisplayInfos) {
        outDisplayInfos.push_back(info.info);
    }
}

那么接下来重点看看layer->fillInputInfo

WindowInfo Layer::fillInputInfo(const InputDisplayArgs& displayArgs) {
//省略部分
//这里就是replaceTouchableRegionWithCrop作用的核心部分
    if (info.replaceTouchableRegionWithCrop) {
        Rect inputBoundsInDisplaySpace;
        if (!cropLayer) {
            FloatRect inputBounds = getInputBounds(/*fillParentBounds=*/true).first;
            inputBoundsInDisplaySpace = getInputBoundsInDisplaySpace(inputBounds, displayTransform);
            ALOGE("name %s !cropLayer  replaceTouchableRegionWithCrop inputBounds left  %f  inputBoundsInDisplaySpace %s"
            ,getName().c_str(),inputBounds.left,android::to_string(inputBoundsInDisplaySpace).c_str());
        } else {
            FloatRect inputBounds = cropLayer->getInputBounds(/*fillParentBounds=*/true).first;
            inputBoundsInDisplaySpace =
                    cropLayer->getInputBoundsInDisplaySpace(inputBounds, displayTransform);
              ALOGE("name %s   replaceTouchableRegionWithCrop inputBounds left  %f  inputBoundsInDisplaySpace %s"
            ,getName().c_str(),inputBounds.left,android::to_string(inputBoundsInDisplaySpace).c_str());

        }
        info.touchableRegion = Region(inputBoundsInDisplaySpace);
         info.touchableRegion.dump(str);
        ALOGE("name %s  replaceTouchableRegionWithCrop touchableRegion  %s",getName().c_str(),str);
    } else if (cropLayer != nullptr) {
        FloatRect inputBounds = cropLayer->getInputBounds(/*fillParentBounds=*/true).first;
        Rect inputBoundsInDisplaySpace =
                cropLayer->getInputBoundsInDisplaySpace(inputBounds, displayTransform);
        info.touchableRegion = info.touchableRegion.intersect(inputBoundsInDisplaySpace);
        ALOGE("name %s no replaceTouchableRegionWithCrop touchableRegion  %s",getName().c_str(),str);
    }


    return info;
}

这个时候就看到了ActivityRecordInputSink那个时候设置的replaceTouchableRegionWithCrop为true的作用了,这里加了相关打印如下:

06-18 16:06:22.349 16704 16704 E Layer   : name 8e3471b ActivityRecordInputSink com.android.messaging/.ui.conversationlist.ConversationListActivity#1647 !cropLayer
  replaceTouchableRegionWithCrop inputBounds left  -10800.000000  
inputBoundsInDisplaySpace Rect(-10799, -19199, 10800, 19200)
06-18 16:06:22.349 16704 16704 D Region  :   Region  (this=0x7ff49f4b08, count=1)
06-18 16:06:22.349 16704 16704 D Region  :     [-10799, -19199, 10800, 19200]

可以看出这里本质就是getInputBounds获取的inputBounds就是很大的数字,那么再看看getInputBounds方法:

std::pair<FloatRect, bool> Layer::getInputBounds(bool fillParentBounds) const {
    Rect croppedBufferSize = getCroppedBufferSize(getDrawingState());
    FloatRect inputBounds = croppedBufferSize.toFloatRect();
    if (hasBufferOrSidebandStream() && croppedBufferSize.isValid() &&
        mDrawingState.transform.getType() != ui::Transform::IDENTITY) {
        inputBounds = mDrawingState.transform.transform(inputBounds);
    }
    ALOGE("getInputBounds 1 inputBounds.left %f ",inputBounds.left);
    bool inputBoundsValid = croppedBufferSize.isValid();
    if (!inputBoundsValid) {
        /**
         * Input bounds are based on the layer crop or buffer size. But if we are using
         * the layer bounds as the input bounds (replaceTouchableRegionWithCrop flag) then
         * we can use the parent bounds as the input bounds if the layer does not have buffer
         * or a crop. We want to unify this logic but because of compat reasons we cannot always
         * use the parent bounds. A layer without a buffer can get input. So when a window is
         * initially added, its touchable region can fill its parent layer bounds and that can
         * have negative consequences.
         */
        inputBounds = fillParentBounds ? mBounds : FloatRect{};
    }
    ALOGE("getInputBounds 2 inputBounds.left %f  mBounds left %f",inputBounds.left,mBounds.left);
    // Clamp surface inset to the input bounds.
    const float inset = static_cast<float>(mDrawingState.inputInfo.surfaceInset);
    const float xSurfaceInset = std::clamp(inset, 0.f, inputBounds.getWidth() / 2.f);
    const float ySurfaceInset = std::clamp(inset, 0.f, inputBounds.getHeight() / 2.f);

    // Apply the insets to the input bounds.
    inputBounds.left += xSurfaceInset;
    inputBounds.top += ySurfaceInset;
    inputBounds.right -= xSurfaceInset;
    inputBounds.bottom -= ySurfaceInset;
    ALOGE("getInputBounds 3 inputBounds.left %f xSurfaceInset = %f",inputBounds.left,xSurfaceInset);

    return {inputBounds, inputBoundsValid};
}

分别也加入了日志,最后打印如下:

06-18 16:06:22.349 16704 16704 E Layer   : getInputBounds 1 inputBounds.left 0.000000 
06-18 16:06:22.349 16704 16704 E Layer   : getInputBounds 2 inputBounds.left -10800.000000  mBounds left -10800.000000
06-18 16:06:22.349 16704 16704 E Layer   : getInputBounds 3 inputBounds.left -10800.000000 xSurfaceInset = 0.000000

可以看到这里inputBounds其实就是Layer的mBounds,那么mBounds来自哪里呢?

这里的mBounds又是哪里设置的呢?其实是parentBounds设置的,最后是如下这个代码设置的

在这里插入图片描述
这里的maxBounds,可以看看获取的它的方法getMaxDisplayBounds

FloatRect SurfaceFlinger::getMaxDisplayBounds() {
    const ui::Size maxSize = [this] {
        ftl::FakeGuard guard(mStateLock);

        // The LayerTraceGenerator tool runs without displays.
        if (mDisplays.empty()) return ui::Size{5000, 5000};

        return std::accumulate(mDisplays.begin(), mDisplays.end(), ui::kEmptySize,
                               [](ui::Size size, const auto& pair) -> ui::Size {
                                   const auto& display = pair.second;
                                   return {std::max(size.getWidth(), display->getWidth()),
                                           std::max(size.getHeight(), display->getHeight())};
                               });
    }();

    // Ignore display bounds for now since they will be computed later. Use a large Rect bound
    // to ensure it's bigger than an actual display will be.
    const float xMax = maxSize.getWidth() * 10.f; //变大10倍
    const float yMax = maxSize.getHeight() * 10.f;

    return {-xMax, -yMax, xMax, yMax};
}


哈哈上面是不是看到久违的宽高放大10倍的Region {-xMax, -yMax, xMax, yMax};

到这里就完美解释清楚了ActivityRecordInputSink巨大区域是哪里来的,本质还是因为replaceTouchableRegionWithCrop设置成了true,在sf计算时候会使用getMaxDisplayBounds作为Region

更多framework实战干货,请关注下面“千里马学框架”

Android InputDispatch事件派发->选择目标窗口 WindowManagerService的主要两大作用: 1 和surfaceflinger交互,创建surface, 通知surfacelinger窗口的层级、大小、位置等属性。 2 和inputflinger交互, 告知inputflinger当前窗口大小位置,是否可以接受input事件以及窗口可以处理什么类型的事件。 surfacelinger和inputflinger都是系统里面两个负载较重的服务。为了分心WindowManagerService我准备先从inputflinger这端着手,因为这端 阅读详情

相关推荐

高效敏感词检测API平台对比与选型指南

本文为开发者提供了高效敏感词检测API平台的选型指南。文章对比了专业API平台、头部云厂商、新兴AI服务商及开源方案,从检测能力、集成易用性、成本、稳定性及扩展性五大维度进行分析,并给出实战选型建议,帮助团队根据业务场景快速选择最合适的敏感词检测服务。

weixin_29325955的博客 347

android认识窗口

这里我按照自己的理解画了一下Android架构图,主要是对看下Framework这块:顶层是基于ApplicationFramework层的Apps层,ApplicationFramework是运行在App进程的Framework代码,如四大组件,各种Manager。在这一层,屏幕上的一块显示区域,典型代表是Activity,但是Activity毕竟是一个综合性比较强的概念,具体到内容显示这块还是由Window类负责,Window则是容纳View对象的容器。

Rainman博的专栏 832

Shader之——花草动画shader

今天 ,接到了个需求,花草的动画。谈到花草动画,立马就想到顶点动画,让顶点动起来就可以实现了。但是有个问题,所有的草都往一个方向运动,这个就麻烦了。因为在shader里没有随机这种概念,提到最多的是用一张噪声图来模拟。在网上搜索了一下,发现有更好的办法,就是用每个顶点到一个点的距离来控制动画的开始,毕竟每个顶点到同一个点的距离很少是一样的。 float dis = distance ...

baicaishisan的博客 2184

ActivityRecordInputSink深入剖析全网独家分析

可以看到Activity的窗口显示大小就是touchableRegion=[0,0][1080,2160]即和屏幕大小一样,而ActivityRecordInputSink大小是touchableRegion=[-10799,-21599][10800,21600]明显比手机屏幕大很多,这种Activity大小和手机手机屏幕一样,触摸事件正常都不可能到这里。上图展示的Activity大小和手机屏幕大小一样,所以手机触摸事件根本到达不了ActivityRecordInputSink这个地方。

liaosongmao1的专栏 517

mysql数据库中使用存储过程带来的好处和示例(存储过程的概念、定义、作用等详解)

MySQL的存储过程(Stored Procedure)是一组SQL语句集合。用户通过指定的存储过程名和参数来调用并执行它。存储过程可以在数据库中预编译并存储,这样它们可以被多次调用而无需重新编写或解析SQL语句。 存储过程将复杂的SQL逻辑封装在一个可重用的单元中。多次调用存储过程,而不需要重复编写相同的SQL代码。- 通过封装,可以隐藏复杂的SQL逻辑,只向用户或应用程序提供简单的接口。MySQL的存储过程和编程语言中的函数的原理和目标是一致的:封装可重用的代码逻辑并提高代码的可维护性和可重用性。

weixin_70208651的博客 1717

快乐8 Ai大模型XGBoost LightGBM预测系统

随机森林回归:通过集成多个决策树模型,提高预测的准确性和稳定性。XGBoost:一种基于梯度提升的强大模型,擅长处理结构化数据。LightGBM:一种高效的基于决策树算法的分布式梯度提升框架,具有快速的训练速度和低内存消耗。高预测准确率:通过集成多种模型,提供更为精准的预测结果。多种预测方法:支持同尾号、重号、温号、连号和综合预测等多种预测策略,满足不同用户的需求。实时日志:在预测过程中,用户可以实时查看系统的预测进度和结果。

m0_72636583的博客 1万+

InputMethodManager输入法窗口为啥dumpsys是全屏?千里马带你疑难解惑输入法相关

可以看到的它的画面大小确实是frame=[0,84][1440,2960],但是有一个touchableRegion=[0,1759][1440,2960],这个区域才是真正的触摸区域,这里我们就看出了猫腻,这里y是从1759的位置才开始可以触摸了,也就是我们上面看到的输入法的键盘区域。层级结构看输入法应该是盖在Activity的窗口上面,而且输入法又是全屏的,那么理论上所有的触摸事件就应该到输入法的窗口,而不应该到Activity的窗口。可以看出这里的setTouchableRegion设置是由触发。

learnframework的博客 778

中科蓝讯蓝牙开发:从源码到固件的自动化构建链路解析

本文详细解析了中科蓝讯AB530X蓝牙芯片从源码到最终固件的完整自动化构建链路。通过剖析prebuild.bat、GCC编译、postbuild.bat等关键环节,阐述了资源打包、配置生成、代码编译与固件合成的全过程,并重点介绍了如何实现一键编译与自动下载,为开发者高效进行嵌入式蓝牙开发提供清晰指南。

weixin_29193259的博客 121

四大组件之ActivityRecord

一. 引言 BroadcastRecord,ServiceRecord都继承于Binder对象,而ActivityRecord并没有继承于Binder。 但ActivityRecord的成员变量appToken的数据类型为Token,Token继承于IApplicationToken.Stub。 appToken:system_server进程通过调用scheduleLaunchActivit...

淡淡的宁静的博客 7728

真双口RAM---Quartus与Vivado开发环境区别对比

2、写和写冲突:表示两个端口写使能同时有效且写地址相同,此时需要关断一个写,把两个写端口都需要更新的值处理到一个写端口上面,任何的DP RAM 都不支持写和写冲突。以上述直方图统计使用为例,博主在仿真过程中发现,vivado真双口RAM--B端口读写延迟2个clk;1、读和写冲突:如果读和写同时有效,且读和写是同一个地址时,发生RAM读写冲突,此时会把最新的写数据直接赋给读数据,称为写穿通到读。

weixin_54470733的博客 3582

OSCP-Challenge 2 - Relia

使用andrea:PasswordPassword_6登录远程桌面,在c盘根目录下发现一个计划任务的ps,文件,修改执行的exe位木马,获取到milana用户的反弹shell。,发现开放了一个8000端口,但是我们外部是访问不到的,它对应的页面是/var/www/internal/,这个目录下的文件我们是可以访问的,把这个文件打个包放到本地分析下。其中数据的可以直接连接mssql,但是端口是49965,不过在数据库中没发现什么可用的信息,但是可以修改密码直接进后台,这个可以先放放。

羽的博客 7768

安卓窗口wms/input小知识NO_INPUT_CHANNEL剖析

NO_INPUT_CHANNEL主要给一些窗口完全不需要消费任何输入事件的情况,经典使用该flag窗口有SnapshotStatingWindow,ActivityRecordInputSink等。

learnframework的博客 1137

【Android 教程系列第 30 篇】为什么要为 App 应用加固 ?如何为 App 应用加固 ?

一:为什么要为 App 应用加固 ? 二:如何为 App 应用加固 ? 三:加固后为什么要重新签名 ?

“Allen Su”的博客 7053

Android Input 系列专题【dumpsys input解读】

Android IMS原理解析 - 简书Android 输入事件分发全流程梳理(一)_android input事件分发流程-优快云博客Android 输入事件分发全流程梳理(二)_android输入事件流程图-优快云博客inputflinger模块与surfaceflinger模块在同级目录之下,即aosp/frameworls/native/services,放在这个目录下面,是因为和fw里面的ims存在大量的交互,其结构如下:

qq_27672101的博客 493

efm32 入门.pdf

本PDF共计22页,介绍的是操作驱动API,下载与否自己斟酌。

win10 跳过U8环境检测的IE WebControls.rar

win10 安装u8的时候 提示IE WebControls 不通过,这个工具可以欺骗u8的检查。 跳过U8环境检测的IE WebControls。提示导入失败后,重新检测通过。

[Android] 输入系统(一)

Android输入系统是人与机器交互最主要的手段。我们通过按键或者触碰屏幕,会先经由linux产生中断,进行统一的处理过后,转换成Android能识别的事件信息,然后Android的输入系统去获取事件,分发给上层用户程序进行处理。 下面在细分一下输入事件在Android系统中的流程: 从图上能看到,输入事件有四个处理的地方: InputReaderThread InputDispa...

weixin_34018202的博客 238

大中型超市收银系统(可直接运行)

光步前台收银系统系统功能:商品销售、商品退货、挂单、收银员权限、会员销售、单品取消、整单取消、商品查询、脱机销售、脱机数据上传,基本资料下载,开钱箱、小票打印、收款结算,支持多币种付款方式(现金、储值卡、礼券、微信等),支持多种促销。运行环境:.net 4.0登陆信息用户名:1001 密码:123456内置商品信息,商品编码可输入00001000 - 00010000,测试效果相关功能键F5 结算 F9挂单 F11商品查询 F8退货 Insert单品取消 Del整单取消 F12开钱箱F2 会员 Ctrl+Z退出系统 Home 锁屏 相关源码待公开

android开发浅谈之ActivityDisplay/ActivityStack/TaskRecord/ActivityRecord

ActivityDisplay/ActivityStack/TaskRecord/ActivityRecord,这几个关键的类,我以前一直搞不清其具体关系,看了android里面的Activity体系结构(1)_ActivityDisplay/ActivityStack/TaskRecord/ActivityRecord这篇文章后,才有了一点点明白,对应也做一个笔记。 整体关系图如下 我直接复制他的图片: 通过adb shell dumpsys activity activities 命令可以看到如下打印:

hfreeman2008的专栏 3383

Statistics and Machine Learning Toolbox

Statistics and Machine Learning Toolbox(带书签),MATLAB统计学习工具

为了讲清楚Android触摸事件,我“拆了部手机”

本篇文章稍微有点长,从硬件驱动,系统内核,到 Framework 都有涉及。其中,了解 IMS 的实现原理,APP 和 IMS 通信的建立,以及 ViewGroup 的 dispatchTouchEvent() 方法的事件派发逻辑,是本篇文章比较重要的内容。

chuyouyinghe的专栏 865

WindowState笔记,windowstate

WindowState笔记,windowstate 这片文章将详细分析WindowState中的各个关键成员变量及成员函数。 1、Session类对象mSession 每一个WindowState都有一个Session类成员变量,那么每一个窗口保存的都是同一个mSession吗?还是每一个窗口的mSession都不同呢?dump一下便知道了,dump日志告诉我上述两种情况都不

3416

学员答疑:安卓分屏窗口的TouchableRegion设置流程追踪

在framework系统是属于一个很复杂的体系,每个小分支都会有很多不同的处理方式等,所以当使用正规的思路打堆栈分析不出来时候,不应该直接放弃,更应该从逆向,或者多角度来尝试探索分析,这样才符合实际项目中遇到各种问题都可以使用学习的知识灵活应对,而不是仅仅套一下模板,一旦有一些异常变化就又不知道如何分析,教给各位粉丝的知识一定要活学活用哈,整体理解多角度分析。更多framework实战干货,请关注下面“千里马学框架”

learnframework的博客 1397

Android6.0 WMS(五) WMS计算Activity窗口大小的过程分析(一)应用进程

转载: https://blog.youkuaiyun.com/kc58236582/article/details/53781071 这篇文章很多参考博客:http://blog.youkuaiyun.com/luoshengyang/article...

jiangnan2222的博客 786

SurfaceFlinger FrontEnd架构

【代码】SurfaceFlinger FrontEnd架构。

wangcheng74520的专栏 880

android输入函数,Android输入系统(4)——InputStage

.使用Java写的Activity程序主要关注输入法处理之前的ViewPreIme和输入法之后的ViewPostIme的处理。2.输入事件传给的控件称为输入焦点。3.每一个Window都有一个DecorView也有一个ViewRootImpl4.java源代码分析使用SourceInsight进行跳转是不合适的,因为跳转是跳转到基类里面去了,调用的函数却是n级派生类的。分析的时候更注重的是哪个类的...

weixin_33181159的博客 357

Android Input之InputChannel

mWindowSession.addToDisplay()最终会调用WMS的addWindow()方法。

探索未知,成就非凡 973
上一篇: 安卓VirtualDisplay虚拟屏幕如何实现没有内容显示mirror内容(aosp14版本)
下一篇: Acitity跳转延时10s会导致anr的时序问题讨论
千里马学框架
博客等级 码龄6年 1万+粉丝 650原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

千里马学框架

帮助你了,就请我喝杯咖啡

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值