哔哩哔哩是如何做Android编译优化的

网络编辑求职招聘QQ群 https://m.sojk.net/yinshijj/26500.html

本期作者

张扬,bilibili资深开发工程师,从事B站AndroidCI/CD开发

背景

哔哩哔哩的安卓项目的工程结构是Monorepo(单仓)变种,也就是所有的代码都在一个工程结构下编译。我们认为Monorepo(单仓)是一个非常适合我们的开发模式,主要是因为其提供的原子提交,可见性,参与度,切片的稳定性等等优点,这些都是我们选择Monorepo的原因。但是因为权限管控,ijkplayer等双端通用工程的原因,还是拆开了多个git仓库。通过git权限的方式来拆分了工程结构,然后通过GradlePlugin的形式进行了多工程的组合,在CI打包环境上让工程具备Monorepo的能力。

随着代码长时间迭代,业务模块数量增多,当前工程有+的模块以及19个复合构建。如果所有的模块都用源代码编译,打一个包本地可能需要大概30分钟的时间才能完成编译。而且哔哩哔哩的安卓工程是一个上百人同时开发的项目,如果一小个改动都需要30分钟的时间投入编译中,对于开发同学来说可能心态都要崩了。

下图是工程在CI上全量编译的情况下,编译耗时大概是16.6分钟,打包机的性能是优于本地电脑的,所以速度会更快一点。

让编译速度变得更快也就迫在眉睫,而且这个模式是针对开发同学,让他们可以快速对模块变更的代码负责。同时也希望这个模式是在不影响当前的工程结构,让他们的打包速度能变得更快。

编译优化

我们通过添加--scan参数,观察了全源码情况下的编译流程。在没有编译缓存的情况下,每个模块都是源代码,所以都需要执行将源代码编译成字节码的过程。同时还需要完成所有工程的依赖配置等等展开,这些过程都是比较耗时的。而工程编译的大部分耗时都集中在将源码编译字节码产物的过程中,也就是GradleTask阶段。下图就是我从buildScan中找出来耗时相对较长的Task任务,可以看出来有的编译任务的耗时在2min以上。

如果能将模块直接变更成aar产物,那么就可以跳过这些模块的编译任务,直接使用他们的二进制产物。但是直接使用aar产物是有风险的,会牺牲一部分代码的正确性,同时代码可能会有滞后性。另外编译阶段会进行很多语法校验的操作,而直接更换成aar产物了就会跳过这部分检查。

而我们快编的做法就是牺牲一部分代码正确性,将没有变更的源代码变成aar产物,在编译阶段直接采用aar产物进行编译,这样就可以跳过部分源代码编译环节。但是因为跳过了源代码编译阶段,所以有一部分编译时的常量优化,方法签名变更或者其他问题就可能会出现。

在快编模式下编译的平均耗时优化到了6min左右。因为ci的特性,每次都需要清空文件夹之后重新clone工程,这样会有额外的工程准备时间。另外在变更的模块比较少的情况下,编译速度则会更快一点。

另外这些问题使用快编的人是否可以接受?我们目的主要是优化下开发编译速度,能让他们可以更快的出包,更快的调试代码,所以我们认为这种取舍还是合理的。

如何可以及时的发现这些方法签名变更的问题呢?下面让我们慢慢展开我们的编译优化方案。

工作流程

我们要先从gradlebuild的生命周期开始展开了,可以分为三个大流程,一个是初始化阶段,一个是配置阶段,另外一个是task执行阶段。一般配置阶段会加载完成工程的依赖关系以及配置信息等工作。

我们会在生命周期的不同阶段,执行一些的代码,然后篡改一些编译相关的属性,从而做到源代码和aar产物之间的替换操作。下面是我列出来我们的项目的快编的工作流程。

andruid是当前的工程名,因为要做多个业务之间的代码隔离,所以我们还是把单个仓库拆分成了多个工程结构,如果一个有所有仓库权限的人或者CI机器可以获取到所有代码仓库的权限,并全源码打包。

babel


转载请注明:http://www.aierlanlan.com/rzfs/767.html

  • 上一篇文章:
  •   
  • 下一篇文章: 没有了