写点什么

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

  • 2021-07-02
  • 本文字数:2779 字

    阅读完需:约 9 分钟

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

一、背景

Android 开发者在日常的开发中,经常需要用到查看视图的功能,Android Studio 开发团队为我们提供了 LayoutInspector 插件。在较新的版本提供了 LiveLayoutInspector,支持 3D,但是不管是 LayoutInspector 还是 LiveLayoutInspector 都非常难用。比如:


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

  • 某些情况无法选中指定的 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.4 效果 &收益

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


LayoutInspector V1: 18803ms

LayoutInspector V2: 328ms


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

三、精确获取点击的 View

3.1 问题描述

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

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

3.2 问题分析

我们首先分析一下 LayoutInspector 的 swing 组件组成:

LayoutInspector 中间图片的预览就是上图中的 myPreview。为了解决这个问题,我们看一下这个点击选中的逻辑。IDEA 自定义插件中使用的 GUI 框架是 Java Swing,组件的鼠标点击、鼠标移入、鼠标退出等事件都可以通过 MouseAdapter 来监听。ViewNodeActiveDisplay 的 MouseAdapter 如下:

查找指定的 View 逻辑:

代码反映出,LayoutInspector 为了满足点击事件消费的顺序,是从后往前遍历的,Z 轴值较大的 View 优先消费事件。但是在很多情况,我们更需要通过比较 View 的面积大小,来选中指定的 View。

3.3 解决方案

其实代码好修复,但是比较麻烦的是,如何替换 ViewNodeActiveDisplay 中 getNode 和 updateSelection 相关逻辑呢,我注意到调用 getNode 的地方都是 click/mouseEnter 等事件,所以我们可以替换掉 MosueAdapter,然后重写 getNode 和 updateSelection。


四、手把手教你搭建 IDEA Plugin 开发环境

修复上述两个痛点需要新建一个 IDEA Plugin,和一般插件开发环境略有不同的是,我们需要依赖 android plugin。

然后在 build.gradle 中添加如下配置:

// See https://github.com/JetBrains/gradle-intellij-plugin/intellij {    localPath = "/Users/xx/Library/Application Support/JetBrains/Toolbox/apps/AndroidStudio/ch-1/202.7231092/Android Studio.app"    plugins = ['android']    updateSinceUntilBuild false}
复制代码
  • localPath 填写你本地的 Android Studio app 路径。

  • 前面我们提到 LayoutInspector 是 android 插件的一部分,所以这里我们声明 plugins = ['android']

五、总结

本文围绕原生 LayoutInspector 的两个痛点,介绍了 LayoutInspector 的工作原理,并提出了解决方案,使得原生 LayoutInspector 稳定、好用。在文章最后也介绍了如何搭建插件工程,方便未接触过插件的新人能进入插件的新世界。


本文转载自:字节跳动技术团队(ID:toutiaotechblog)

原文链接:如何打造稳定、好用的 Android LayoutInspector?

2021-07-02 07:001812

评论

发布
暂无评论
发现更多内容

这份1658页的Java面试核心突击讲,成功让我上岸阿里

小二,上酒上酒

Java 程序员 面试 阿里 大厂面试

Spring Boot 3.0 正式发布,这份升级指南必须码住

程序知音

Java spring 微服务 springboot 后端技术

信息论与编码:信道的定义和分类

timerring

11月月更 信息论 信道

使用RPA机器人快速实现表格数据汇总

YonBuilder低代码开发平台

开发者

互联网大厂必问之MySQL、Redis、Spring三大块,面试必备技术栈

钟奕礼

Java java面试 java编程 程序员‘

又一创新!阿里云 Serverless 调度论文被云计算顶会 ACM SoCC 收录

阿里巴巴云原生

阿里云 Serverless 云原生

2022最新整理上千道Java面试攻略,近500页PDF文档

钟奕礼

Java Java 面试 java程序员 java编程

阿里大牛纯手写的微服务入门笔记,从基础到进阶直接封神

小二,上酒上酒

Java 编程 程序员 架构 微服务

小伙伴面经分享京东+面试八股文整套面试真题(含答案)

钟奕礼

Java 程序员 java面试 java编程

微服务调用的正确打开方式

Java全栈架构师

Java 程序员 面试 微服务 后端

测试自动化中遵循的最佳实践

禅道项目管理

自动化测试

个推TechDay治数训练营直播预告 | 从方法论到落地应用,详解企业标签体系建设要点

个推

标签 用户画像 标签体系

个推发布《Android13适配指南》,解读Android13新特性

个推

android 安卓 安卓开发

阿里云张建锋:核心云产品全面 Serverless 化

Serverless Devs

星策转型大咖说第二弹!前喜茶数字化副总裁、前百果科技首席技术市场官沈欣老师数字化转型经验分享!

星策开源社区

开源 方法论 转型 智能化转型

KnowStreaming贡献流程

石臻臻的杂货铺

kafka 后端 11月月更

Java岗史上最全八股文面试真题汇总,堪称2022年面试天花板

小二,上酒上酒

Java 程序员 面试 八股文

自学 UI 设计有哪些书籍推荐

千锋IT教育

适用更多会议场景,华为云会议的分组讨论功能来了!

IT科技苏辞

华为云开发者官网首页焕新升级,赋能开发者云上成长

华为云开发者联盟

华为云

【计算讲谈社】第十三讲|未来40年,“碳中和”可能带来哪些深远影响?

大咖说

碳中和

Go语言—big包的使用

良猿

Go golang 后端 11月月更 goweb

华为云会议网络研讨会,按次订购更方便!

清欢科技

工业物联网DCS和SCADA的区别

2D3D前端可视化开发

物联网 DCS web组态软件 SCADA 工业组态

音频“黑科技”上新,华为云会议让“云端”声音更真切!

爱尚科技

数据技术前沿趋势、TiDB 产品方向、真实场景 Demo… 丨PingCAP DevCon 2022 产品技术论坛预览

PingCAP

TiDB

Spring Boot 3.0 正式发布,这份升级指南必须码住

程序知音

Java spring 微服务 springboot 后端技术

个推TechDay直播回顾 | 详解数据指标体系设计与开发全流程(附视频及课件下载)

个推

数据运营 指标预测 数据指标体系

待办事项是什么意思,为什么要用?

优秀

待办事项

ShareSDK for Flutter

MobTech袤博科技

阿里P8大佬总结的Nacos入门笔记,从安装到进阶小白也能轻松学会

小二,上酒上酒

Java 编程 程序员 nacos

如何打造稳定、好用的 Android LayoutInspector?_语言 & 开发_字节跳动技术团队_InfoQ精选文章