如何打造稳定、好用的 Android LayoutInspector?

  • 速度极慢,遇到复杂的布局经常超时

  • 某些情况无法选中指定的 View

本文将围绕 LayoutInspector 的痛点,分析问题并修复,最终将 LayoutInspector 变成一个稳定、好用的插件。

二、加速 Dump View Hierarchy


2.1 问题描述

开发复杂业务的同学在使用 LayoutInspector 时都遇到过上图所示的错误:由于 View 树结构复杂超时。网上也有其他相关的解决办法,原理就是修改 timeout 的值,目前默认值是 20s,所以改成 1min,大概率是可以的了。

为了更好的解决这个问题,比如是否能加速?我们看一下整个 LayoutInspector 抓取的流程。梳理流程之前,我们需要找到功能的入口。

2.2 问题分析

2.2.1 Dump 总流程

平常开发者使用 LayoutInspector 的流程一般如下:

  1. 和 Attach debugger 类似,先获取要 LayoutInspector 的进程

  2. 如果进程中不止一个 ViewRootImpl,还需要选择 window

在 IDEA Plugin 框架体系中,大多数插件的功能入口都依赖 Action,上图 LayoutInspector 的功能入口对应的 Action 如何找到呢?最快速、准确的办法就是 Debug,在我们点击功能入口之前,在 AnAction#actionPerformed 加上断点。

从 AndroidRunLayoutInspectorAction 出发,我们找到了真正的任务:

LayoutInspectorCaptureTask。

抓取 View 视图的关键方法如下:

我们可以看到这里先构造了一个 Options,Opentions 中有个参数:ProtocolVersion,目前我们能使用的是 ProtocolVersion.Version1,Goolge 内可以通过 StudioFlags 打开 ProtocolVersion.Version2。

capture view 的流程会比较长,涉及到 adb 通信原理,我们先简单了解一下 adb 通信架构。

  • adb server: 运行在我们的 PC 开发机上,监听 5037 端口

  • adb daemon: 运行在 Android 设备上

  • adb server 通过 USB/tcp 和 adbd 通信

了解了基本的 adb 通信基础之后,我们再来看整个 captureview 的原理:

  1. 通过 ClientWindow 发起 loadWindowData 的请求(在这里可以看到默认超时时间是 20s)

  2. ClinetImpl 收到请求,让 HandleViewDebug 将本次请求封装成 JDWP,然后准备发送

  3. ClientImpl 将数据先发送给本 PC 上的 adb server

  4. adb server 将数据通过 usb/tcp 透传给 Android 设备上的 adbd

  5. Android 设备上的 adbd 根据之前选择的进程信息,将信息再透传给指定的 jdwp 线程

  6. jdwp 通过 native 调用 DDMServer 方法

  7. DdmHandleViewDebug 收到请求开始处理

  8. 处理完请求后,再通过 socket 返回,LayoutInspector 收到结果解析后展示

参考:debugger.cc

  • https://android.googlesource.com/platform/art/+/android-cts-5.0_r9/runtime/debugger.cc#3778

2.2.2 dump v1 原理

在上图的流程中可以看到在最后的调用中,有 dump 和 dumpv2 两个方法,而且 dump 方法已经废弃了。

源码 ViewDebug.java:

看源码我们知道 v1 dump 是获取被 @ExportedProperty 注解作用的 filed 和 method,然后将这些数据写入 ByteArrayOutputStream。比如 View的 padding 属性:

当然也有 method:

上面两图中的 ​
category: padding 和 focus 体现在 LayoutInspector 的属性面板中:

上面看源码的结论:v1 是通过反射遍历所有的 Filed 和 Method。

在我的手机 One Plus7 Android 10 上,View 的 filed 有 487 个,method 有 915 个。写一段简单的代码展示一下仅遍历耗时:

输出:

D/View#dump: 10705ms and 692 views

可以看到我们还没有添加逻辑,仅仅遍历耗时都达到了 10s。

2.2.3 dump v2 原理

看 ViewDebug#dumpv2:

调用到了 View#encode:

相比 v1,v2 就很克制了,只返回有限的数据,需要什么数据就获取什么数据,但不支持自定义的属性,相当于牺牲了一定的灵活性,加快了 dump 的速度。在灵活性、速度两个方面,Google 将 v1 和 v2都保留了,并通过 StudioFlags 提供了开关。

2.3 解决方案

对比完 v1 和 v2 之后,基本可以确定 v2 的速度会快很多了。我们通过自定义 Action,并替换掉原生的 LayoutInspectorCaptureTask,关键是替换下面这个方法:

2.3 效果&收益

v2 相比 v1 速度快了非常多,下面贴一下抖音直播间的 Dump 数据,设备:One Plus 7 Android 10.

LayoutInspector V1: 18803ms

LayoutInspector V2: 328ms

本章节介绍了如何使用 v2 dump 协议来加速,下面介绍第二个痛点:某些情况无法选中指定的 View。

三、精确获取点击的 View

3.1 问题描述

LayoutInspector 还有一个不尽人意的地方——无法选中指定的 View。举个例子:

上图蓝框其实是一个空白的没有内容的 View,这个蓝框盖在了「收礼」这个红圈上。在我们点击这个红圈的时候,却是选中的蓝框。

最后

小编这些年深知大多数初中级Android工程师,想要提升自己,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助

因此我收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人

都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

资料⬅专栏获取
点击这个红圈的时候,却是选中的蓝框。

最后

小编这些年深知大多数初中级Android工程师,想要提升自己,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助

因此我收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。

[外链图片转存中…(img-CsurT5bh-1719100969448)]一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人

都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

资料⬅专栏获取

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【STM32-启动文件 startup_stm32f103xe.s】

STM32-启动文件 startup_stm32f103xe.s ■ STM32-启动文件■ STM32-启动文件主要做了以下工作:■ STM32-启动文件指令■ STM32-启动文件代码详解■ 栈空间的开辟■ 栈空间大小 Stack_Size■ .map 文件的详细介绍■ 打开map文件 ■ 堆空间■ PRESERVE8 和 THUMB 指令…

用Java获取键盘输入数的个十百位数

这段Java代码是一个简单的程序,用于接收用户输入的一个三位数,并将其分解为个位、十位和百位数字,然后分别打印出来。下面是代码的详细解释: 导入所需类库: import java.util.Scanner;:导入Scanner类,用于从…

使用Gradle查看Android项目中库的依赖关系

| | -- com.android.support:support-compat:25.3.1 | | | — com.android.support:support-annotations:25.3.1 | | -- com.android.support:support-media-compat:25.3.1 | | | -- com.android.support:support-annotations:25.3.1 | | | — com.android.support:support…

Windows10中端口被占用处理方法

前言 在Windows 10中,查看端口被占用情况的方法主要依赖于命令行工具netstat。以下是详细步骤,以及必要的解释和归纳: 打开命令提示符 方法1:使用快捷键Win R,打开“运行”对话框,输入cmd,然…

深入讲解C++基础知识(一)

目录 一、基本内置类型1. 类型的作用2. 分类3. 整型3.1 内存描述及查询3.2 布尔类型 —— bool3.3 字符类型 —— char3.4 其他整型 4. 有符号类型和无符号类型5. 浮点型6. 如何选择类型7. 类型转换7.1 自动类型转换7.2 强制类型转换7.3 类型转换总结 8. 类型溢出8.1 注意事项 …

5.XSS-反射型(post)利用:获取cookie

原理: 文件路径:\pikachu\pkxss\xcookie\post.html 将post.html文件,复制到皮卡丘的根路径下或者根下随意路径即可,并编辑文件 需要修改以下两个地址,第一个地址是将原界面的样子链接过来,让用户认为是原…

数据结构十三:2 - 3树和红黑树

一开始就接触这五点,会让人云里雾里,不利于了解这个数据结构。因为这种先给定义在推导的方式并不适合学习。它没有介绍红黑树的来源,而只是给你生硬的定义。 而学习红黑树的最好学习资料就是大名鼎鼎的《算法4》,如下&#xff1a…

微型操作系统内核源码详解系列五(四):cm3下svc启动任务

系列一:微型操作系统内核源码详解系列一:rtos内核源码概论篇(以freertos为例)-CSDN博客 系列二:微型操作系统内核源码详解系列二:数据结构和对象篇(以freertos为例)-CSDN博客 系列…

LabVIEW电控旋翼测控系统

开发基于LabVIEW开发的电控旋翼测控系统,通过高效监控和控制提升旋翼系统的性能和安全性。系统集成了多种硬件设备,采用模块化设计,实现复杂的控制和数据处理功能,适用于现代航空航天领域。 项目背景 传统旋翼系统依赖机械和液压…

算法04 模拟算法之一维数组相关内容详解【C++实现】

大家好,我是bigbigli,模拟算法我们将分为几个章节来讲,今天我们只看一维数组相关的题目 目录 模拟的概念 训练:开关灯 解析 参考代码 训练:数组变化 解析 参考代码 训练:折叠游戏 解析 参考代码 …

vscode+picgo+gitee实现Markdown图床

vscode中编辑Markdown文件,复制的图片默认是保存在本地的。当文档上传csdn时,会提示图片无法识别 可以在gitee上创建图床仓库,使用picgo工具上传图片,在Markdown中插入gitee链接的方式来解决该问题。 一、 安装picgo工具 1.1 v…

IOS开发学习日记(十六)

目录 App间的唤起和通信 App跳转 通过Scheme唤起其他App Universal Link 组件化 App间的唤起和通信 App跳转 使用URL Scheme支持App启动、跳转及参数传递 分享 / 登陆 / 拉起App Store等 设置URL Type 在UIApplication中处理参数和业务逻辑 -(BOOL)application:(UIApp…

强化安全新篇章:韶关石油化工可燃气体报警器年检解析

韶关,这座位于广东省北部的城市,近年来在石油化工行业取得了显著的发展。 随着一批批大型石化企业的进驻和投产,韶关不仅成为了区域性的石化产业基地,也为地方经济带来了强劲的增长动力。 然而,随着石化产业的快速发…

Selenium WebDriver - 网络元素

本文翻译整理自:https://www.selenium.dev/documentation/webdriver/elements/ 文章目录 一、文件上传二、定位策略1、传统定位器2、创建定位器3、类名4、CSS选择器5、id6、NAME7、链接文本8、部分链接文本9、标签名称10、xpath11、相对定位器它是如何工作的可用相对…

2024最新版Python 3.12.4安装使用指南

2024最新版Python 3.12.4安装使用指南 2024最新版Python 3.12.4安装使用指南0. Python的受欢迎程度1. 安装最新版Python 3.12.42. 验证Python 3.12.4版本3. 验证Python功能4. 使用IDLE交互式开发模式5. 安装Python扩展库相关阅读: By Jackson 2024最新版Python 3.12…

【每日刷题】Day73

【每日刷题】Day73 🥕个人主页:开敲🍉 🔥所属专栏:每日刷题🍍 🌼文章目录🌼 1. 2583. 二叉树中的第 K 大层和 - 力扣(LeetCode) 2. 1325. 删除给定值的叶子…

为 Android 应用打造精良的 Chrome OS 使用体验

override fun onKeyUp(code: Int, ev: KeyEvent?): Boolean { return when (code) { KeyEvent.KEYCODE_J -> { // Do something here true } else -> super.onKeyUp(code, ev) // 重要!! } } 注意我们标出 “重要” 的那一行代码。这行代…

同步FIFO

描述 根据题目提供的双口RAM代码和接口描述,实现同步FIFO,要求FIFO位宽和深度参数化可配置。 电路的接口如下图所示。 端口说明如下表。 双口RAM端口说明: 端口名 I/O 描述 wclk input 写数据时钟 wenc input 写使能 waddr input…

分布式架构的优势与实现

目录 前言1. 什么是分布式架构1.1 分布式架构的定义1.2 分布式架构的基本原理 2. 分布式架构的优势2.1 可扩展性2.2 容错性和高可用性2.3 性能优化2.4 灵活性和可维护性 3. 分布式架构的实现方法3.1 服务拆分3.1.1 功能拆分3.1.2 垂直拆分3.1.3 水平拆分 3.2 数据分布与存储3.2…