Android代码编译提速

255 阅读4分钟

1.序言

Android项目一般使用Gradle做为构建打包的工具,执行速度一直为人所诟病,对于较大型的项目工程来说,全量编译一次的时间可能高达十来分钟,增量编译一次的时间大概是三四分钟。尤其是当项目有Java和Kotlin混写的时候更是令人头痛。尤其是在需求后期修复BUG的场景,往往一行代码的改动也需要等待很久,这严重的影响了开发体验和团队效率。针对这一情况做出优化就显得非常关键。

2.方案

1.头条

1.公司内部搭建maven私服,在私服上设置代理仓库(google mavenCentral等),关闭缓存开关。

收益:不可控

2.建立仓库组,将所有的仓库放到一个统一的仓库中,依赖时只需要从组仓库中查找,这样能大大降低多次网络请求的耗时。

收益:不可控

3.模块aar化,将所有的模块发布成aar,在项目中默认通过maven依赖这些编译过的组件,而在修改某个模块时,通过配置项将该模块的依赖形式改为源码依赖,做到编译时只编译改动的模块。

收益:全量编译耗时从7,8分钟降低到4,5分钟,收益将近50%,效果显著 缺点:模块需要拆分很细,如果模块代码量非常大,提升并不明显。

4.fast增量编译,禁用原始的javac/kotlinCompile等task,自己实现代码增量修改判断,只编译修改的代码。动态禁用kapt相关的task,降低kapt,kaptGenerateStub等task的耗时。

收益:将编译维度降低至文件级。增量编译从2分多降低至20~30s 缺点:需要适配kapt注解、java\kotlin语法糖的脱糖、大量的字节码插桩等等 都会提升编译的耗时。

2.QQ音乐

增量编译:

1.获取改动文件并进行编译(在首次全量编译的时候 收集所有生成的class,放到缓存目录中。通过javac\kotlinc 进行改动文件的编译)。

2.对代码依赖分析,修改了方法签名 除了需要编译改动的类,依赖这个类的其他类,也是需要重新编译的,否则,就会在运行期出现异常(在收集所有生成的class时 对类的内部结构进行分析,提取出关键信息 在增量编译的时候根据之前提取的信息 获取到依赖集,启动第二轮编译)。

3.资源编译,首次编译之后收集编译好的资源二进制文件 放入缓存目录。后续改动会根据这些文件 覆盖掉原来的文件。打成资源包 推送到手机上。

收益:编译维度降低至文件级,但是对三方的支持会需要自行适配,例如AGP版本的升级,三方库(databinding)等

3.有赞

增量编译:

1.获取改动文件进行编译(通过Watchman对某个文件夹做文件改动监控,并支持使用命令获取改动的文件路径信息)。

2.过滤改动文件 通过Flavor 过滤实际需要编译的文件

3.获取工程信息(遍历.idea下方的libraries目录解析xml获取对应的依赖),获取工程对应的信息 packageId等。

4.通过javac\kotlinc 实现增量编译

4.aapt2 进行资源编译

收益:编译维度降低至文件级,但是对三方的支持会需要自行适配,例如AGP版本的升级,三方库(databinding)等

基本上重头戏的优化都在增量编译上,目前主流的做法都是Gradle插件,通过Gradle Hook编译流程,提取出关键信息,进行增量编译。

3.效果

项目1是参考开源项目WordPress:github.com/wordpress-m… 项目带有kt,java,compose 开启了DataBinding ViewBinding

修改的文件:

java/org/wordpress/android/ui/accounts/LoginActivity.java

image.png

res/layout/login_activity.xml image.png

image.png 代码部署耗时:3m51s

使用插件之后:

image.png 代码部署耗时:13.6s

项目2是参考开源项目Telegram:github.com/DrKLO/Teleg…

修改的文件:java/org/telegram/ui/QrActivity.java java/org/telegram/ui/Cells/DrawerActionCell.java

image.png 代码部署耗时:1m44s

使用插件之后: image.png 代码部署耗时:1.9s

这是我的电脑配置:

image.png

效果还是蛮明显的哈~ 后边再讲原理,插件稍后会上到Marketplace。欢迎大家一起下载,有问题我会及时解决的~