【问题分析】InputDispatcher无焦点窗口ANR问题【Android 14】

在这里插入图片描述

1 问题描述

Monkey跑出的无焦点窗口的ANR问题。

特点:

1)、上层WMS有焦点窗口,为Launcher。

2)、native层InputDispacher无焦点窗口,上层为”recents_animation_input_consumer“请求了焦点,但是”recents_animation_input_consumer“最终没有成为焦点窗口,原因是”NO_WINDOW“。

2 log分析

在这里插入图片描述

3 复现ANR

从log分析中可以看到发生ANR的场景的上下文:

1)、首先是SystemUI主线程比较卡顿的情况下,这种的我们要复现问题可能需要给SystemUI的绘制加点延迟。

2)、先启动了一个Activity,“com.tct.nxtvision_ui/.LauncherActivity”。

3)、随后屏幕转为了180度,即ROTATION_2。

4)、输入KeyEvent.KEYCODE_RECENT_APPS(312),切换到Launcher的Recents,并且屏幕转为0度,即ROTATION_0。

5)、接着在焦点App切换到Launcher之前,又输入了一个KeyEvent.KEYCODE_BACK(4),并且是第一步启动Activity接收到了,并且调用了finish —— KO!!!后续native层的InputDispacher出现了无焦点窗口的情况。

4 原因分析

4.1 NO_WINDOW代码分析

回到当时看log时,分析ANR出现的直接原因:

在这里插入图片描述

这里InputDispatcher侧的焦点窗口从“recents_animation_input_consumer”切走,直接原因为“NO_WINDOW”,查看到具体代码位置,FocusResolver.setInputWindows:

在这里插入图片描述

关键的地方有:

大概的内容为,局部变量currentFocus代表的是存储的焦点窗口,而windows代表的是从SurfaceFlinger处传入的最新的可接收输入事件的输入窗口列表,这里继续调用FocusResolver.getResolvedFocusWindow去寻找一个焦点窗口,并且调用FocusResolver.updateFocusedWindow来更新焦点窗口。

继续看FocusResolver.getResolvedFocusWindow:

在这里插入图片描述

这里的逻辑主要是调用FocusResolver.isTokenFocusable来判断某一个窗口是否能够作为焦点窗口:

在这里插入图片描述

这里要遍历的windows是从SurfaceFlinger发来的最新的输入窗口列表,而token是之前设置的焦点窗口,这个函数的大意是根据最新的输入窗口信息判断之前设置的焦点窗口是否有效。

那么可能有几种情况,即对应Focusability的4个值:

在这里插入图片描述

1)、返回NO_WINDOW,说明之前设置的焦点窗口已经不在最新的输入窗口列表里了,即该输入窗口的Layer已经被移除了,或者不满足Layer.needsInputInfo的条件。

2)、返回NOT_FOCUSABLE,说明之前设置的焦点窗口还在最新的输入窗口列表里,但是被设置了NOT_FOCUSABLE这个标志位,不满足作为焦点窗口的条件了。

3)、返回NOT_FOCUSABLE,说明之前设置的焦点窗口还在最新的输入窗口列表里,但是被设置了NOT_VISIBLE,即该Layer已经不可见了,所以不能再作为焦点窗口了。

4)、返回OK,找到了一个符合条件的窗口作为焦点窗口,并且将该窗口保存在传参outFocusableWindow中。

从上面的代码分析可知,焦点窗口从“recents_animation_input_consumer”切走的原因为它对应的Layer已经被移除了,或者不满足Layer.needsInputInfo的条件,继续打开DebugConfig.DEBUG_FOCUS这个开关继续看看log,发现有:

在这里插入图片描述

原因为“Window went away”,是该Layer被移除了。

4.2 recents_animation_input_consumer分析

首先看下正常情况下这个Layer的信息为:

在这里插入图片描述

它的zOrderRelativeOf就是被transientHide的那个Task。

该Layer(SurfaceControl)在创建INPUT_CONSUMER_RECENTS_ANIMATION对应的Input ConsumerImpl对象中创建:

在这里插入图片描述

显示、隐藏和移除的逻辑为:

在这里插入图片描述

4.3 “Window went away”原因分析

复现问题后,dumpsys SF的信息,发现:

在这里插入图片描述

“recents_animation_input_consumer”对应的Layer仍然存在,那为什么没有遍历到呢?

查看SurfaceFlinger向InputDispatcher更新输入窗口的地方,SurfaceFlinger.updateInputFlinger:

在这里插入图片描述

1)、SurfaceFlinger.updateInputFlinger方法的逻辑比较简单,调用SurfaceFlinger.buildWindowInfos构建一个输入窗口列表,然后发给InputDispatcher,那么关键的地方就在于SurfaceFlinger.buildWindowInfos了,它是如果选择哪个Layer可以作为输入窗口的?

2)、SurfaceFlinger.buildWindowInfos的逻辑也很简单,主要是通过Layer.needsInputInfo来判断一个Layer是否能够作为输入窗口的:

在这里插入图片描述

应该是主要是判断Layer.hasInputInfo,而“recents_animation_input_consumer”在show的时候已经设置了一个InputWindowInfo了,所以原因应该不是这个。

继续添加log,发现的确如此,“recents_animation_input_consumer”没有发送给InputDispatcher的原因是“recents_animation_input_consumer”这个Layer根本就没有遍历到!

原因则是:

在这里插入图片描述

“recents_animation_input_consumer”的相对层级的那个Task已经从Layer层级结构中被移除了,这个Task都遍历不到了,自然“recents_animation_input_consumer”也遍历不到了。

所以这个问题的根本原因是:

1)、在native层,“recents_animation_input_consumer”已经不能作为焦点窗口了,因为transientHide的那个Task已经被移除了。

2)、在上层WMS处,“recents_animation_input_consumer”仍然可以作为一个焦点窗口去请求焦点,没有考虑到transientHide的Task此时是否已经被移除。

再看下WMS处为“recents_animation_input_consumer”请求焦点的逻辑:

在这里插入图片描述

主要就是这个成员变量mActiveRecentsActivity:

在这里插入图片描述

当Recents界面被调起,就为mActiveRecentsActivity为Launcher对应的ActivityRecord,并且激活“recents_animation_input_consumer”来作为焦点窗口。

比如,当我们点击Recents的时候,会启动一个Transition:

1)、在Transition.onTransactionReady阶段,会调用Transition.handleLegacyRecentsStartBehavior来设置InputMonitor.mActiveRecentsActivity为Launcher对应的ActivityRecord。

2)、在Transition.finishTransition阶段,会将InputMonitor.mActiveRecentsActivity置为null。

在我们复现ANR的场景:

1)、在native层SF,由于和“recents_animation_input_consumer”绑定的那个transientHide的那个Task已经被移除了,所以“recents_animation_input_consumer”就不能作为焦点窗口了。

2)、在上层WMS,因为仍然处于Recents界面,这个Transition一直都不会finish,那么InputMonitor.mActiveRecentsActivity就一直不为空,那么每次走到InputMonitor.updateInputFocusRequest的时候,就会为“recents_animation_input_consumer”请求焦点。

这么看来是InputMonitor.updateInputFocusRequest为“recents_animation_input_consumer”请求焦点的这段逻辑有点问题。

5 在pixel上复现ANR

那我们如何在pixel上复现呢?从以上分析可知这个问题似乎是google原生问题,SystemUI卡顿并非复现该场景的必要条件,毕竟本题中SystemUI卡顿带来的效果的本质是,推迟Launcher称为焦点App的时间,从而让输入KEYCODE_RECENT_APPS后再次输入的KEYCODE_BACK能够被“com.tct.nxtvision_ui/.LauncherActivity”接收到做finish的操作。

那么我们需要让我们模拟的App,在输入KEYCODE_RECENT_APPS切换到Launcher后自动finish,不需要为SystemUI增加延迟,似乎也可以复现“recents_animation_input_consumer”请求不到焦点的情况。

最终果然可行,pixel的Launcher,“com.google.android.apps.nexuslauncher/.NexusLauncherActivity”也可以复现:

在这里插入图片描述

finish的代码为:

    @Overridepublic void onTopResumedActivityChanged(boolean isTopResumedActivity) {super.onTopResumedActivityChanged(isTopResumedActivity);if (!isTopResumedActivity) {Handler handler = new Handler();handler.postDelayed(new Runnable() {@Overridepublic void run() {finish();}}, 4000);}}

可以看到复现问题的路径更简单了,再次总结一下:

1)、首先是SystemUI主线程比较卡顿的情况下,这种的我们要复现问题可能需要给SystemUI的绘制加点延迟。

2)、写一个Demo App,启动“com.example.demoapp/.MainActivity”,为其设置方向android:screenOrientation=“reversePortrait”,这样它一启动屏幕就转为ROTATION_180(当然用adb命令让屏幕转成180也行,monkey应该就是这么做的,没什么区别)。

3)、输入KeyEvent.KEYCODE_RECENT_APPS(312),这将切换到Launcher的Recents,并且屏幕转为0度,即ROTATION_0。

4)、让“com.example.demoapp/.MainActivity”在切换到Launcher的4s后调用finish —— KO!!!后续native层的InputDispacher出现了无焦点窗口的情况,如果再发一个KeyEvent,我一般发KeyEvent.KEYCODE_BUTTON_C(98),比如就会复现ANR,log就如上面的贴的所示。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/577214.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Abaqus2024混凝土随机多边形及界面层插件AbyssFish_RandomPolygon2D V2版本更新

很高兴地向大家宣布, AbyssFish_RandomPolygon2D插件已经迎来了重要更新!本次更新旨在适配Python3版本的Abaqus2024,并优化插件界面,提升用户体验。同时我们为所有已购买旧版本插件的用户提供免费升级服务! 插件介绍…

ESP32

MicroPython 是运行在微控制器硬件之上的完全的Python编译器和运行时系统。提供给用户一个交互式提示符(REPL)来立即执行所支持的命令。 你们那么 。 交互式提示符(REPL)是MicroPython提供给用户的一种界面,它允许用户在…

【QT入门】 QListWidget各种常见用法详解之图标模式

往期回顾 【QT入门】 Qt代码创建布局之多重布局变换与布局删除技巧-CSDN博客 【QT入门】 QTabWidget各种常见用法详解-CSDN博客 【QT入门】 QListWidget各种常见用法详解之列表模式-CSDN博客 【QT入门】 QListWidget各种常见用法详解之图标模式 QListWidget有列表和图标两种显…

《C++ Primer 第五版 中文版》第12章 动态内存【阅读笔记 + 个人思考】

《C Primer 第五版 中文版》第12章 动态内存【阅读笔记 个人思考】 12.1 动态内存与智能指针12.1.1 shared_ptr类 静态内存包括:初始化只读数据段,初始化读写数据段,未初始化数据和常量数据段。 详细在下面博客总结: Linux系统下…

矩阵的归一化技术

矩阵的归一化(Normalization)是将矩阵中的元素缩放到一个特定的范围或者标准,使得在进行比较、评估或计算时能够保持数值稳定性和可比性。这个过程在数据预处理、机器学习、图像处理等领域中非常重要。归一化有助于改善算法的收敛速度和精度&…

Lumos学习python第七课:字符串

一、老规矩,题目呢? 1、题目-----来源力扣 罗马数字包含以下七种字符: I, V, X, L,C,D 和 M。 字符 数值 I 1 V 5 X 10 L 50 C …

LeetCode_1.两数之和

一、题目描述 二、方法 1.方法1&#xff08;暴力枚举法&#xff09; 利用两个for循环&#xff0c;对数组进行逐一的遍历&#xff0c;直到找到两个数的和为目标值时返回这两个数的下标。以下为c实现的完整代码。 # include<iostream> using namespace std; #include<…

消息队列经典应用场景

笔者心中,消息队列,缓存,分库分表是高并发解决方案三剑客。 在职业生涯中,笔者曾经使用过 ActiveMQ 、RabbitMQ 、Kafka 、RocketMQ 这些知名的消息队列 。 这篇文章,笔者结合自己的真实经历,和大家分享消息队列的七种经典应用场景。 1 异步&解耦 笔者曾经负责某电…

戴尔灵越3000来说2.5G的双核显存能干啥?

吃鸡已经成为大家耳熟能详的网络游戏。 很多人认为&#xff0c;想要享受吃鸡的乐趣&#xff0c;就必须组装一台高端电脑。 虽然配置越高越好&#xff0c;但现实是很多配置都是以性能为标准的。 有余了&#xff0c;没必要刻意追求高配置、高特效。 说实话&#xff0c;吃鸡不一定…

Linux之文件系统

我们之前谈到的文件描述符fd,是与被加载到内存中的文件相关的&#xff0c;那么还有什么文件呢&#xff1f;磁盘文件 内存文件 ------ 断电失效 磁盘文件 ------ 不受断电的影响 磁盘存储器存、取信息的最基本单位是扇区。 —个扇区能存储512Bytes的数据,OS与磁盘交互的单位…

CSS及javascript

一、CSS简介 css是一门语言&#xff0c;用于控制网页的表现。 cascading style sheet:层叠样式表 二、css的导入方式 css代码与html代码的结合方式 &#xff08;1&#xff09;css导入html有三种方式&#xff1a; 1.内联样式&#xff1a;<div style"color:red&quo…

视频素材免费哪个好?7个视频素材下载网站推荐

小伙帮们准备做视频的时候才发现&#xff0c;哎呀&#xff0c;高清视频素材哪里找啊&#xff1f;不用急&#xff0c;这次我们依旧从中国的宝藏网站开始&#xff0c;然后穿越全球&#xff0c;发现更多精彩的无水印视频素材网站 1&#xff0c;蛙学府&#xff08;中国&#xff09…