android源码编译速度-Android编译速度提升黑科技——RocketX

怎么做编译优化,当时说了一个方案,就是编译时把所有模块依赖都改成aarandroid源码编译速度,然后每次编译都把改了的模块改成源代码依赖,编译后把改了的模块作为aar上传,这样你就可以始终只让最少的模块参与源代码编译,从而提高编译速度。

当然,说起来容易,做起来却不那么容易。 最后有小伙伴将上面的描述开发成了一个开源的解决方案,非常值得学习和借鉴。

一、背景说明

随着项目的规模越来越大,编译速度也会下降。 有时一个更改需要等待几分钟的编译时间。

基于这些常见情况,RocketX应运而生。 通过在编译过程中动态改变项目依赖,并用aar动态替换模块,只编译改变的模块,其他模块不参与编译。 无需更改原项目任何代码,完整编译得到提升。 速度。

2、效果展示 2.1. 测试项目介绍

目标项目共有3W+个类和资源文件,完整编译耗时约4分钟(测试使用2018年mbp第8代i7 16g)。

RocketX完全成长后的疗效(每次操作取3次平均值)。

项目依赖关系如右图所示。 App依赖于bm业务模块,bm业务模块依赖于顶层base/comm模块。

依赖关系

当base/comm模块发生变化时,底层的所有模块都必须参与编译。 因为app/bmxxx模块可能使用了base模块中的socket或者变量,不知道是否有变化。 (然后速度就很慢了)

修改bmDiscover后,只需app模块和bmDiscover两个模块参与编译即可。 (快点)

rx(RocketX)的编译速度无论哪个模块基本都控制在30s左右,因为只编译了app和修改的模块,其他模块都是不参与编译的aar包。

顶层模块速度提升300%+ 3.思维问题分析及模块构建 3.1. 思维问题分析

需要通过gradle插件动态地将未修改的模块依赖更改为对应的aar依赖。 如果模块发生改变,就会退化为project项目依赖,这样每次只编译改变的模块和app。

您需要将implement/api moduleB更改为implement/api aarB。

需要建立本地maven存储中未更改的模块对应的aar。 (也可以换成flatDir,速度更快)

编译过程开始,需要找到哪个模块被改变了。

需要遍历各个模块的依赖关系进行替换。 如何获取模块依赖关系? 是否可以一次性获取所有模块依赖,还是每个模块都反弹? 修改其中一个模块依赖会阻止之前模块依赖的反弹?

每个模块改成aar后,其所依赖的子依赖(网络依赖,aar)都给了父模块(如何找到所有父模块)? 还是直接进入应用程序模块? 是否存在断开应用程序与模块依赖关系的风险? 这里需要一个技术解决方案。

需要hook编译过程,完成后将修改后的aar替换到本地maven中。

提供AS状态栏按钮来实现开/关功能,加快编译速度或允许开发者使用长期以来习惯的三角形运行按钮。

android源码编译速度-Android编译速度提升黑科技——RocketX

3.2. 模块构建

根据前面的分析,虽然问题较多,但整个项目大致可以分为以下几个部分:

4.问题解决与实现 4.1、实现 源码实现入口是DynamicAddDependencyMethods中的tryInvokeMethod方法。 他是一个动态语言的方法缺失函数。

TryInvoke方法代码分析:

 public DynamicInvokeResult tryInvokeMethod(String name, Object... arguments) {       //省略部分代码 ...       return DynamicInvokeResult.found(this.dependencyAdder.add(configuration, normalizedArgs.get(0), (Closure)null)); }

dependencyAdder 实现是 DirectDependencyAdder。

private class DirectDependencyAdder implements DependencyAdder {    private DirectDependencyAdder() {    }    public Dependency add(Configuration configuration, Object dependencyNotation, @Nullable Closure configureAction) {        return DefaultDependencyHandler.this.doAdd(configuration, dependencyNotation, configureAction);    }}

最后在DefaultDependencyHandler.this.doAdd中添加,项目中即可获取到DefaultDependencyHandler。

  DependencyHandler getDependencies(); 

通过上面的分析,添加对应的aar/jar可以通过以下代码实现。

fun addAarDependencyToProject(aarName: String, configName: String, project: Project) {    //添加 aar 依赖 以下代码等同于 api/implementation/xxx (name: 'libaccount-2.0.0', ext: 'aar'),源码使用 linkedMap    if (!File(FileUtil.getLocalMavenCacheDir() + aarName + ".aar").exists()) return    val map = linkedMapOf()    map.put("name", aarName)    map.put("ext", "aar")    // TODO: 2021/11/5 改变依赖 这里后面需要修改成    //project.dependencies.add(configName, "com.${project.name}:${project.name}:1.0")    project.dependencies.add(configName, map)}

android源码编译速度-Android编译速度提升黑科技——RocketX

4.2. localMave 更喜欢使用 flatDir 通过指定缓存目录来实现生成的 aar/jar 包,并在依赖发生变化时通过搜索来替换。

fun flatDirs() {    val map = mutableMapOf()    map.put("dirs", File(getLocalMavenCacheDir()))    appProject.rootProject.allprojects {        it.repositories.flatDir(map)    }}

4.3. 当编译过程开始时,需要找出哪个模块被改变了。

使用遍历整个项目的文件的lastModifyTime来做实现。

以每个模块为粒度,递归遍历当前模块的文件,整合估算每个文件的lastModifyTime,得到唯一的标记countTime。

通过将countTime与上次进行比较,相同则表示没有变化,不同则表示发生变化。 并且预估的countTime需要同步到本地缓存。

3W个文件整体拿1.2s还是可以接受的。

4.4. 模块依赖获取。

下面的代码可以用来寻找生成整个项目的依赖图的机会,在这里生成一个依赖图解析器。

 project.gradle.addListener(DependencyResolutionListener listener)

4.5. 将模块依赖项目替换为aar技术方案

各模块依赖替换的遍历顺序是乱序的,因此技术方案需要支持乱序替换。

android源码编译速度-Android编译速度提升黑科技——RocketX

目前采用的解决方案是:如果当前模块A没有改变,则需要通过localMaven将A替换为A.aar,并将A.aar和A的子依赖交给一级父模块。 (可能被指责如果父模块也是aar怎么办,其实这个没有问题,这里就不展开了,篇幅太长了)

为什么应该将其提供给家长而不是直接提供给应用程序? 这是一个简单的例子。 如果B.aar没有给出A模块,那么A使用B模块的socket就丢失了,会导致编译失败。

给出整体项目替换的技术方案演示:

4.5. hook编译过程,完成后将修改后的aar替换到本地maven中。

点击三角形run,执行的命令是app:assembleDebug,需要在assembleDebug前面添加一个uploadLocalMavenTask,通过finalizedBy运行我们的任务来同步变化的aar

4.6. 提供AS状态栏按钮,小火箭按钮,一个喷火一个非喷火,代表启用/禁用,还有一把扫帚清理rockectx的缓存。

5 三天的小惊喜5.1。 我发现当我点击运行按钮时,执行的命令是app:assembleDebug,并且aar并没有打包在各个子模块的输出中。

解决方案:通过研究gradle源码,发现打包是通过任务bundleFlavor{Flavor}Flavor{BuildType}Aar来执行的,那么只需要找到各个模块对应的任务,将其注入到app:assembleDebug中即可运行。

5.2. 运行后发现存在多个jar包重复问题。

解决方案:执行fileTree(dir: “libs”, include: ["*.jar"]) jar依赖不能交给父模块,jar包会进入到aar中的lib中,可以直接移除。 可以通过下面的代码来确定:

// 这里的依赖是以下两种: 无需添加在 parent ,因为 jar 包直接进入 自身的 aar 中的libs 文件夹//    implementation rootProject.files("libs/xxx.jar")//    implementation fileTree(dir: "libs", include: ["*.jar"])childDepency.files is DefaultConfigurableFileCollection || childDepency.files is DefaultConfigurableFileTree

5.3. 发现aar/jar有多个依赖。

实现(名称:'libXXX',扩展名:'aar')

实现文件(“libXXX.aar”)

解决方案:使用第一个,第二个会合并到aar中,导致类重复。

5.4. 发现aar的新坐姿依赖性。

configurations.maybeCreate("default")artifacts.add("default", file('lib-xx.aar'))

上面的代码使 aar 成为其他模块依赖项的单独模块。 默认配置实际上是模块最终输出aar的持有者。 默认config可以保存aar的列表,因此手动将aar添加到默认config中也相当 是当前模块中打包的产物。

解决方案:通过childProject.configurations.maybeCreate("default").artifacts找到所有添加的aar,单独发布localmaven。

5.5. 发现android模块可以打包成jar。

解决方案:找到名为jar的任务,并在jar任务后面注入uploadLocalMaven任务。

5.6. 发现arouter有bug,transform没有通过outputProvider.deleteAll()清除旧缓存。

解决方案:检查问题的详细信息。 至此,arouter问题解决了,代码也合并了。 但是没有新的插件版本发布到mavenCentralandroid源码编译速度,所以我先帮arouter解决了。

收藏 (0) 打赏

感谢您的支持,我会继续努力的!

打开微信/支付宝扫一扫,即可进行扫码打赏哦,分享从这里开始,精彩与您同在
点赞 (0)

悟空资源网 源码编译 android源码编译速度-Android编译速度提升黑科技——RocketX https://www.wkzy.net/game/161540.html

常见问题

相关文章

官方客服团队

为您解决烦忧 - 24小时在线 专业服务