移动APP质量优化框架 - Booster

文导读|   点击标题阅读

互联网寒冬下,程序员如何突围提升自己?

Android 技术选型

“37岁,年薪50万,一夜被裁”:伪上班,毁掉了多少中国年轻人

作者:booster (项目地址见文末)
链接:https://juejin.im/post/5cea22ebf265da1bb564d4d7
简介

Booster 是一款专门为移动应用设计的易用、轻量级且可扩展的质量优化框架,其目标主要是为了解决 APP 复杂度的提升而带来的性能、稳定性、包体积等问题。

为什么是 Booster?

质量优化是所有应用开发者都要面临的问题,对于 DAU 千万级的 APP 来说,万分之一的崩溃率就意味着上千的用户受到影响,对于长时间在线的司机来说,司机端 APP 的稳定性关乎着司机的安全和收入,所以更是不容小觑。

随着业务的快速发展,业务复杂度不断提升,我们开始思考:

  • 如何持续保证 APP 的质量?

  • 当 APP 崩溃后,如何快速定位问题所属的业务线?

  • 能不能在上线之前提前发现潜在的质量问题?

  • 能不能对 APP 进行无侵入的全局质量优化而不需要推动各个业务线?

基于这些考虑,Booster 应运而生,经过一年多的时间不断打磨,Booster 成绩斐然。由于目前在质量优化方面基于静态分析的开源项目屈指可数,加上质量优化对于 APP 开发者而言门槛偏高,因此,我们选择了将 Booster 开源,希望更多的开发者和用户能从中受益。

功能与特性

动态加载模块

为了支持差异化的优化需求,Booster  实现了模块的动态加载,以便于开发者能在不使用配置的情况下选择使用指定的模块,详见:booster-task-all、booster-transform-all。

第三方类库注入

Booster 在进行优化的过程中,可能需要注入一些特定的类或者类库,为了解决注入类的依赖管理问题,Booster 提供了VariantProcessor SPI 让开发者可以轻松的扩展,请参考:ThreadVariantProcessor.kt#L12

性能检测

APP 的卡顿率是衡量应用运行时性能的一个重要指标,为了能提前发现潜在的卡顿问题,Booster 通过静态分析实现了性能检测,并生成可视化的报告帮助开发者定位问题所在,如下图所示:

640?wx_fmt=other

其实现原理是通过分析所有的 class 文件,构建一个全局的 Call Graph, 然后从 Call Graph 中找出在主线程中调用的链路(Application、四大组件、View、Widget等相关的方法),然后再将这些链路以类为单位分别输出报告。

详见:booster-transform-lint。

多线程优化

业务线众多的 APP 普遍存在线程过载的问题,而线程管理一直是开发者最头疼的问题之一,虽然可以通过制定严格的代码规范来归避此类问题发生,但是对于组织结构复杂的大厂来说,实施起来成本巨大,而对于第三方 SDK 来说,代码规范则有些力不从心。为了彻底的解决这一问题,Booster 通过在编译期间修改字节码实现了全局线程池优化,并对线程进行重命名。

以下是示例代码:

线程重命名效果如下图所示:

640?wx_fmt=other

详见:booster-transform-thread

SharedPreferences 优化

对于 Android 开发者来说,SharedPreferences 几乎无处不在,而在主线程中修改 SharedPreferences 会导致卡顿甚至 ANR,为了彻底的解决这一问题,Booster 对 APP 中的指令进行了全局的替换。

详见:booster-transform-shared-preferences。

常量字段删除

无论是资源索引,还是其它常量字段,在编译完成后,就没有存在的价值了(反射除外),因此,Booster 将对资源索引字段访问的指令替换为常量指令,将其它常量字段从类中删除,一方面可以提升运行时性能,另一方面,还能减小包体积,资源索引(R)表面上看起来微不足道,实际上占用不少空间,以滴滴车主为例,资源索引相关的类就有上千个,进行常量字段删除后,减小了 1MB 左右。

详见:booster-transform-shrink。

Toast Bug 修复

为了彻底解决在 Android 7.1 中存在的bug: 30150688,Booster 对 APP 中的 Toast.show() 方法调用指令进行全局替换。

详见:booster-transform-toast。

资源压缩

APP 的包体积也是一个非常重要的指标,在 APP 安装中,图片资源占了相当大的比例,通常情况下,图片质量降低 10%-20% 并不会影响视觉效果,因此,Booster 采用有损压缩来降低图片的大小,而且,图像尺寸越小,加载速度越快,占用内存越少。
Booster 提供了两种压缩方案:

  • pngquant 有损压缩(需要自行安装 pngquant 命令行工具)

  • cwebp 有损压缩(已内置)

两种方案各有优缺点,pngquant 的方案不存在兼容性问题,但是压缩率略逊于 WebP,而 WebP 存在系统版本兼容性问题,总的来看,有损压缩的效果非常显著,以滴滴车主为例,APP 包体积减小了 10 MB 左右。

另外,像 Android Support Library 中包含有大量的图片资源,而且支持多种屏幕尺寸,对于 APP 而言,相同的图片资源,保留最大尺寸的即可。以 Android Support Library 为例,去冗余后,APP 包体积减小了 1MB 左右。

详见:booster-task-compression。

WebView 预加载

为了解决 WebView  初始化导致的卡顿问题,Booster 通过注入指令的方式,在主线程空闲时提前加载 WebView。

除上以上特性外,Booster 还提供了一些辅助开发的功能,如:检查依赖项中是否包含 SNAPSHOT 版本等等。

快速入门

在 buildscript 的 classpath 中引入 Booster 插件,然后启用该插件:

 
  

buildscript {
    ext.booster_version = '0.4.3'
    repositories {
        google()
        mavenCentral()
        jcenter()
    }
    dependencies {
        classpath "com.didiglobal.booster:booster-gradle-plugin:$booster_version"
        classpath "com.didiglobal.booster:booster-task-all:$booster_version"
        classpath "com.didiglobal.booster:booster-transform-all:$booster_version"
    }
}

apply plugin: 'com.android.application'
apply plugin: 'com.didiglobal.booster'

然后通过执行 assemble task 来构建一个优化过的应用包:

 
  

$ ./gradlew assembleRelease

构建完成后,在 build/reports/ 目录下会生成相应的报告:

 
  

build/reports/
├── booster-task-compression
│   └── release
│       └── report.txt
├── booster-transform-lint
│   └── release
│       ├── com
│       └── org
├── booster-transform-shared-preferences
│   └── release
│       └── report.txt
├── booster-transform-shrink
│   └── release
│       └── report.txt
├── booster-transform-thread
│   └── release
│       └── report.txt
└── booster-transform-toast
    └── release
        └── report.txt

项目地址:https://github.com/didi/booster

更多学习和讨论,欢迎加入我们的知识星球,这里有1000+小伙伴,让你的学习不寂寞~·

94年12月,我们小分队在滇西北找矿。小分队一共8人,其中4名警战士每人配备一支冲锋枪。一天,出发前,一位纳西族老乡搭我们的车去维西。那天路上积雪很大,雪下的路面坑洼不平,车子行驶一段就会被雪坞住。我们不得不经常下来推车。就在我们又一次下车推车的时候,一群褐黄色的东西慢慢向我们靠近。我们正惊疑、猜测时,纳西族老乡急喊:“快、快赶紧上车,是一群狼。”司机小王赶紧发动车,加大油门……但是很不幸,车轮只是在原地空转,根本无法前进。这时狼群已靠近汽车……大家看得清清楚楚——8只狼,个个都象小牛犊似的,肚子吊得老高。战士小吴抄起冲锋枪,纳西族老乡一手夺下小吴的抢。比较沉着地高声道:“不能开枪,枪一响,它们或钻到车底下或钻进树林,狼群会把车胎咬坏,把我们围起来,然后狼会嚎叫召集来更多的狼和我们拼命。”他接着说:“狼饿疯了,它们是在找吃的,车上可有吃的?”我们几乎同声回答:“有。”“那就扔下去给它们吃。”老乡像是下达命令。从来没有经历过这样的事,当时脑子里一片空白,除了紧张,大脑似乎已经不会思考问题。听老乡这样说,我们毫不犹豫,七手八脚把从丽江买的腊肉、火腿还有十分珍贵的鹿子干巴往下丢了一部分。狼群眼都红了,兴奋地大吼着扑向食物,大口的撕咬吞咽着,刚丢下去的东西一眨眼就被吃光了。老乡继续命令道:“再丢下去一些!”第二批大约50斤肉品又飞出了后车门,也就一袋烟的工夫,又被8只狼分食的干干净净。吃完后8只狼整齐地坐下,盯着后车门。这时,我们几人各个屏气息声,紧张的手心里都是冷汗,甚至能够清晰的听到自己心跳的声音……我们不知道能有什么办法令我们从狼群中突围出去。看到这样的情形,老乡又发话道:“还有吗?一点不留地丢下,想保命就别心疼这些东西了!”此时,除了紧张、害怕还有羞愤……!作为战士,我们是有责任保护好这些物资的,哪怕牺牲自己。但是现实情况是我们的车被坞到雪地里出不来,只能被困在车里。我们的子弹是极有限的,一旦有狼群被召唤来,我们会更加束手无策。我们几人相互看了一眼,迟疑片刻,谁也没有说什么,忍痛将车上所有的肉品,还有十几包饼干全都甩下车去!8只狼又是一顿大嚼。吃完了肉,它们还试探性的嗅了嗅那十几包饼干,但没有吃。这时我清楚地看到狼的肚子已经滚圆,先前暴戾凶恶的目光变得温顺。其中一只狼围着汽车转了两圈,其余7只狼没动。片刻,那只狼带着狼群朝树林钻去......不可思议的事情发生了……不一会儿,8只狼钻出松林,嘴里叼着树枝,分别放到汽车两个后轮下面。我们简直不敢相信自己的眼睛……这些狼的意思是想用树枝帮我们垫起轮胎,让我们的车开出雪窝。我激动地大笑起来……哈……哈……刚笑了两声,另外一个战士忙用手捂住了我的嘴,他怕这突兀的笑声惊毛了狼。接着,8只狼一齐钻到车底,但见汽车两侧积雪飞扬。我眼里滚动着泪花,大呼小王:“狼帮我们扒雪呢,赶快发动车,”车启动了,但是没走两步,又打滑了。狼再次重复刚才的动作:“先往车轮下垫树枝,然后扒雪……”。就这样,每重复一次,汽车就前进一段,大约重复了十来次。最后一次,汽车顺利地向前行了一里多地,接近了山顶。再向前就是下坡路了。这时,8只狼在车后一字排开坐着,其中一只比其他7只狼稍稍向前。老乡说:“靠前面的那只是头狼,主意都是他出的。”我们激动极了,一起给狼鼓掌,并用力地向它们挥手致意。但是这8只可爱的狼对我们的举动并没有什么反应,只是定定地望了望我们,然后,头狼在前,其余随后,缓缓朝山上走去,消失在松林中......看完不忍思考:连凶猛的狼都懂得报恩,我们是否应该反思自身?类,我们是不是应当让这个世界充满爱?。小分队一共8人,其中4名警战士每人配备一支冲锋枪。一天,出发前,一位纳西族老乡搭我们的车去维西。那天路上积雪很大,雪下的路面坑洼不平,车子行驶一段就会被雪坞住。我们不得不经常下来推车。就在我们又一次下车推车的时候,一群褐黄色的东西慢慢向我们靠近。我们正惊疑、猜测时,纳西族老乡急喊:“快、快赶紧上车,是一群狼。”司机小王赶紧发动车,加大油门……但是很不幸,车轮只是在原地空转,根本无法前进。这时狼群已靠近汽车……大家看得清清楚楚——8只狼,个个都象小牛犊似的,肚子吊得老高。战士小吴抄起冲锋枪,纳西族老乡一手夺下小吴的抢。比较沉着地高声道:“不能开枪,枪一响,它们或钻到车底下或钻进树林,狼群会把车胎咬坏,把我们围起来,然后狼会嚎叫召集来更多的狼和我们拼命。”他接着说:“狼饿疯了,它们是在找吃的,车上可有吃的?”我们几乎同声回答:“有。”“那就扔下去给它们吃。”老乡像是下达命令。从来没有经历过这样的事,当时脑子里一片空白,除了紧张,大脑似乎已经不会思考问题。听老乡这样说,我们毫不犹豫,七手八脚把从丽江买的腊肉、火腿还有十分珍贵的鹿子干巴往下丢了一部分。狼群眼都红了,兴奋地大吼着扑向食物,大口的撕咬吞咽着,刚丢下去的东西一眨眼就被吃光了。老乡继续命令道:“再丢下去一些!”第二批大约50斤肉品又飞出了后车门,也就一袋烟的工夫,又被8只狼分食的干干净净。吃完后8只狼整齐地坐下,盯着后车门。这时,我们几人各个屏气息声,紧张的手心里都是冷汗,甚至能够清晰的听到自己心跳的声音……我们不知道能有什么办法令我们从狼群中突围出去。看到这样的情形,老乡又发话道:“还有吗?一点不留地丢下,想保命就别心疼这些东西了!”此时,除了紧张、害怕还有羞愤……!作为战士,我们是有责任保护好这些物资的,哪怕牺牲自己。但是现实情况是我们的车被坞到雪地里出不来,只能被困在车里。我们的子弹是极有限的,一旦有狼群被召唤来,我们会更加束手无策。我们几人相互看了一眼,迟疑片刻,谁也没有说什么,忍痛将车上所有的肉品,还有十几包饼干全都甩下车去!8只狼又是一顿大嚼。吃完了肉,它们还试探性的嗅了嗅那十几包饼干,但没有吃。这时我清楚地看到狼的肚子已经滚圆,先前暴戾凶恶的目光变得温顺。其中一只狼围着汽车转了两圈,其余7只狼没动。片刻,那只狼带着狼群朝树林钻去......不可思议的事情发生了……不一会儿,8只狼钻出松林,嘴里叼着树枝,分别放到汽车两个后轮下面。我们简直不敢相信自己的眼睛……这些狼的意思是想用树枝帮我们垫起轮胎,让我们的车开出雪窝。我激动地大笑起来……哈……哈……刚笑了两声,另外一个战士忙用手捂住了我的嘴,他怕这突兀的笑声惊毛了狼。接着,8只狼一齐钻到车底,但见汽车两侧积雪飞扬。我眼里滚动着泪花,大呼小王:“狼帮我们扒雪呢,赶快发动车,”车启动了,但是没走两步,又打滑了。狼再次重复刚才的动作:“先往车轮下垫树枝,然后扒雪……”。就这样,每重复一次,汽车就前进一段,大约重复了十来次。最后一次,汽车顺利地向前行了一里多地,接近了山顶。再向前就是下坡路了。这时,8只狼在车后一字排开坐着,其中一只比其他7只狼稍稍向前。老乡说:“靠前面的那只是头狼,主意都是他出的。”我们激动极了,一起给狼鼓掌,并用力地向它们挥手致意。但是这8只可爱的狼对我们的举动并没有什么反应,只是定定地望了望我们,然后,头狼在前,其余随后,缓缓朝山上走去,消失在松林中......看完不忍思考:连凶猛的狼都懂得报恩,我们是否应该反思自身?自诩为“万物灵长”的人类,我们是不是应当让这个世界充满爱?

看完本文有收获?请转发分享给更多人


我们的知识星球第三期开期了,已达到1100人了,能连续做三期已很不容易了,有很多老用户续期,目前续期率达到50%,说明了大家对我们的知识星球还是很认可的,欢迎大家加入尽早我们的知识星球,更多星球信息参见:

欢迎加入Java和Android架构社群

如何进阶成为Java的Android版和架构师?

说两件事

640?wx_fmt=jpeg

微信扫描或者点击上方二维码领取的Android \ Python的\ AI \的Java等高级进阶资源

更多学习资料点击下面的“阅读原文 ”获取

640?wx_fmt=gif

谢谢老板,点个好看↓

猜你喜欢

转载自blog.csdn.net/xJ032w2j4cCjhOW8s8/article/details/91470846
今日推荐