序
在原理篇中,我们发现在App内存的分布中,Code是占大头的部分,所以我们可以从App体积方面想办法,通过减小App体积达到降低内存的目的,同时,根据权威的机构分析,体积与用户下载和留存有很大的联系,总之体积减小有很大的好处,本篇研究了一下滴滴开源的Booster工具,减小包体积,优化App性能。
1. Booster简介
1.1 是什么
Booster 是滴滴开源的一款专门为移动应用设计的易用、轻量级且可扩展的质量优化框架,其目标主要是为了解决随着 APP 复杂度的提升而带来的性能、稳定性、包体积等一系列质量问题。
Booster 提供了性能检测、多线程优化、资源索引内联、资源去冗余、资源压缩、系统 Bug 修复等一系列功能模块,可以使得稳定性能够提升 15% ~ 25%,包体积可以减小 1MB ~ 10MB。
Booster 主要由 Transformer 和 Task 组成,Transformer 主要用于对字节码进行扫描或修改(取决于 Transformer 的功能),Task 主要用于构建过程中的资源处理,为了满足特异的优化需求,Booster 提供了 Transformer SPI and VariantProcessor SPI允许开发者进行定制,以下是 Booster 的整体框架:
1.2 能做什么?
-
性能检测:使用 Booster 可以发现潜在的性能问题,例如,在应用中调用可能阻塞 UI 线程或者主线程的 API,如,I/O API 等;
-
性能优化:对于开发者来说,线程管理一直是个头疼的问题,特别是第三方 SDK 中的线程,过多的线程可能会导致内存不足,然而幸运的是,这些问题都能通过 Booster 来解决;
-
系统问题修复:例如全局性地修复 Android API 25 版本中 Toast 导致的崩溃;
-
应用瘦身:如,资源压缩及冗余资源删除、资源索引内联及常量删除;
1.3 目前Booster内置的一些功能
-
动态加载模块支持差异化的优化需求,Booster 实现了模块的动态加载,以便于开发者能在不使用配置的情况下选择使用指定的模块,详见:booster-task-all、booster-transform-all,也可以定制task和transform,然后设置classpath。
-
第三方类库注入:支持动态添加依赖或者注入某些类和库(比如插桩、无埋点统计等),详见:booster-transform-lint。
-
多线程优化:业务线众多的 APP 普遍存在线程过载的问题,而线程管理一直是开发者最头疼的问题之一,虽然可以通过制定严格的代码规范来规避此类问题发生而对于第三方 SDK 来说,代码规范则有些力不从心。为了彻底的解决这一问题,Booster 通过在编译期间修改字节码实现了全局线程池优化,并对线程进行重命名。详见:booster-transform-thread。
-
SharedPreferences 优化:SharedPreferences几乎无处不在,而在主线程中修改 SharedPreferences 会导致卡顿甚至 ANR,为了彻底的解决这一问题,Booster 对 APP 中的指令进行了全局的替换。详见:booster-transform-shared-preferences。
-
常量字段删除:无论是资源索引,还是其它常量字段,在编译完成后,就没有存在的价值了(反射除外),因此,Booster 将对资源索引字段访问的指令替换为常量指令,将其它常量字段从类中删除,一方面可以提升运行时性能,另一方面,还能减小包体积,资源索引(R)表面上看起来微不足道,实际上占用不少空间。
-
资源压缩:APP 的包体积也是一个非常重要的指标,在 APP 安装中,图片资源占了相当大的比例,通常情况下,图片质量降低 10%-20% 并不会影响视觉效果,因此,Booster 采用有损压缩来降低图片的大小,而且,图像尺寸越小,加载速度越快,占用内存越少。Booster 提供了两种压缩方案:
-
pngquant 有损压缩(需要自行安装 pngquant 命令行工具)
-
cwebp 有损压缩(已内置)
-
-
性能检测:APP 的卡顿率是衡量应用运行时性能的一个重要指标,为了能提前发现潜在的卡顿问题,Booster 通过静态分析实现了性能检测,并生成可视化的报告帮助开发者定位问题所在,其实现原理是通过分析所有的 class 文件,构建一个全局的 Call Graph, 然后从 Call Graph 中找出在主线程中调用的链路(Application、四大组件、View、Widget等相关的方法),然后再将这些链路以类为单位分别输出报告,详见:booster-transform-lint。
-
WebView 预加载:为了解决 WebView 初始化导致的卡顿问题,Booster 通过注入指令的方式,在主线程空闲时提前加载 WebView。
2. 引入使用Booster
2.1 地址
-
开源地址:https://github.com/didi/booster/tree/master
-
官方Doc:https://booster.johnsonlee.io/zh/guide/
2.2 引入
集成 Booster 的最佳方式是集成真正需要的模块来解决项目中遇到的特定问题。
buildscript {ext.boosterVersion = '4.8.0'repositories {google()mavenCentral()// OPTIONAL If you want to use SNAPSHOT version, sonatype repository is required.maven { url 'https://oss.sonatype.org/content/repositories/public' }}dependencies {classpath "com.didiglobal.booster:booster-gradle-plugin:$boosterVersion" // ① booster基础插件// ② 弄清楚真正需要的特性,选择正确的模块进行集成,下面的是我们的工程中目前引入的模块classpath "com.didiglobal.booster:booster-task-compression-cwebp:$boosterVersion" // 采用 cwebp 对资源进行压缩classpath "com.didiglobal.booster:booster-task-compression-processed-res:$boosterVersion" // ap_ 文件压缩classpath "com.didiglobal.booster:booster-task-resource-deredundancy:$boosterVersion" // 去冗余资源classpath "com.didiglobal.booster:booster-transform-r-inline:$boosterVersion" // 资源索引内联classpath "com.didiglobal.booster:booster-transform-thread:$boosterVersion" // 性能优化解决线程过多问题classpath "com.didiglobal.booster:booster-transform-shared-preferences:$boosterVersion" // SharedPreferences 优化,解决卡顿问题}
}allprojects {repositories {google()mavenCentral()// OPTIONAL If you want to use SNAPSHOT version, sonatype repository is required.maven { url 'https://oss.sonatype.org/content/repositories/public' }}
}// 在你的 application 工程的build.gradle中引入,如下:
apply plugin: 'com.android.application'
apply plugin: 'com.didiglobal.booster' // ③
2.3 确认是否启用
然后在终端用如下命令来确认 Booster 是否启用:
./gradlew assembleDebug --dry-run
2.4 版本选择
2.4.1 官方文档上说
由于 AGP 8 的不兼容性变更,AGP 7.x 及以下版本已经不再支持,如果你仍在使用 AGP 7.x,请使用 Booster 4.x
大部分基于
Task
的模块在 Booster 5.0.0 中已经不再支持,但是基于Transform
的模块仍然支持且没有破坏性变更。详情请参见 [从 Booster 4.x 迁移到 5.x](迁移到 v5.x | Booster
2.4.2 我们的选择
我们使用booster主要是为了减小包体积,其中重要的一项是资源压缩(booster-task-compression-cwebp),测试合适的版本,经过验证,4.8.0版本是能支持的最高版本,再高的版本就会没有我们需要的Task(booster-task-compression-cwebp)。
所以我们选择了能编译过去,并且有我们需要的Task的最高版本 4.8.0。
2.5 功能组件选择
经过查找测试,决定使用以下6个插件进行优化:
-
booster-task-compression-cwebp(采用 cwebp 对资源进行压缩)
-
booster-task-compression-processed-res(ap_ 文件压缩)
-
booster-task-resource-deredundancy(去冗余资源)
-
booster-transform-r-inline(资源索引内联)
-
booster-transform-thread(性能优化解决线程过多问题)
-
booster-transform-shared-preferences(SharedPreferences 优化,解决卡顿问题)
2.6 booster-task-compression-cwebp插件的参数配置
文档说明如下:
Property | Description | Example |
| compression quality (the default is 80) | |
| ignore wildcards (separated by comma) |
|
经过实际测试发现
-
booster.task.compression.cwebp.quality
-
发现不生效,查看代码,应该设置为:booster.task.compression.cwebp.option.quality,验证可用;
-
-
booster.task.compression.cwebp.ignores
-
按文档中写的示例配置不生效;
-
经验证,白名单需要遵循以下规则:
-
资源名(png图片)后没有点和扩展名,直接文件名结尾
-
资源名前应该还有其它字符,但测试未得到正确的结果,需要再研究,现阶段可以使用通配符*
-
多个白名单资源以逗号分隔,逗号前后不能有空格
-
白名单不能以双引号或单引号包裹
-
白名单太长折行时应以反斜杠续行,如下
-
booster.task.compression.cwebp.ignores=*food_card_bg,*food_\pic_bg_noa,\*frame_fridge_520_36,\*frame_fridge_550_37,\*frame_fridge_777_36
-
-
编译booster源码,打印日志查看:使用带日志的自己打出来的snapshot版的booster插件,看到Cwebp白名单过滤时,传入的资源字符串的形式是“drawable/food_card_bg”,与之前验证出来的pattern规则能对应上。
-
3. 总结
本文简述了Booster的功能和其引入的方法,然后应用到我们的项目中发现的一些问题及解决方法,其它的暂未进行深入研究,后续可以引入更多的Task或Transform,也可以扩展我们自己的功能。
另外,研究Booster的过程中,发现字节也开源了一个类似的框架ByteX,后续也可以一起研究一下,取长补短,只要是对项目有用的可以逐步引入,没有的功能也可以通过扩展实现。
4. 参考文献
-
开源地址
-
官方Doc
-
滴滴开源 Booster:移动APP质量优化框架
-
滴滴Booster移动APP质量优化框架 学习之旅
-
Booster 移动 APP 质量优化框架
5.团队介绍
「三翼鸟数字化技术平台-定制平台开发」主要负责设计工具的研发,包括营销设计工具、家电VR设计和展示、水电暖通前置设计能力,研发并沉淀素材库,构建家居家装素材库,集成户型库、全品类产品库、设计方案库、生产工艺模型,打造基于户型和风格的AI设计能力,快速生成算量和报价;同时研发了门店设计师中心和项目中心,包括设计师管理能力和项目经理管理能力。实现了场景全生命周期管理,同时为水,空气,厨房等产业提供商机管理工具,从而实现了以场景贯穿的B端C端全流程系统。