1.1.背景在我们平时的开发中,构建项目是决定开发效率的基础环节。但是随着业务代码的不断积累,编译时间也随之增加。的增长。虽然已经有很多增量编译加速的方案,但不可避免的是在很多场景下,还是需要全量编译。至于全量编译加速,我们遇到了一些困难:在K歌项目中,总代码量160万行,kotlin代码约占43%,编译时间高达70%,必须压缩。所以我们需要想办法既能继续享受kotlin带来的开发便利,又能缓解全编译时间快速增长的问题。1.2.如果解决方案可以减少kotlin编译次数,则可以减少编译时间。要么减少代码,要么提前编译代码,后者可行性高。Android支持两种二进制归档文件:JAR和AAR,源码以.class格式存在,但jar不包含资源,对做组件化的项目不友好,会直接生成library模块编译后aar。然后,只要把所有的库模块通过CI/CD工具,不断自动生成aar,发布到同一个maven仓库,编译时用这些aar参与编译,就可以了成功的。1.3.方案初步呈现库模块提前编译成aar,我们需要将依赖类型从implementationproject改为implementationaar。如果库模块代码发生变化,需要提前反复编译aar,修改依赖的版本号,很浪费时间。能否禁止这种重复操作环节,使程序自动化?更好的办法:在编译时判断库模块当前代码版本是否有可用的aar,如果有则使用aar参与编译。反汇编过程:计算当前代码版本中所有文件的hash,包括:JavaSourceJavaResourceAssetsResourcesAidlJniLibsAndroidManifest.xmlProguardLint判断maven仓库中是否有hash对应的aar,地址=repository/libraryName/version-md5修改library模块依赖类型为aar。1.4.遇到的问题1.JarDuplicateClassConflict可以看到B对C有直接依赖,这个关系会在B.arr的元数据文件.pom中声明,因为C的代码变了,无法匹配到远程aar,所以最后C会以aar和project两种方式参与编译,如果C中包含jar会冲突。2.项目重复类冲突share_m和share是同一个代码仓库。在开发过程中很容易验证和更改名称。路由不同,代码相同。Gradle认为是两个aar,又报错了。3.三方库版本冲突最终编译后share代码版本还是1.2.0,因为B.aar依赖share:1.2.0。Gradle将考虑所有请求的版本,无论它们出现在依赖图中的什么位置。在这些版本中,它将选择最高版本。第一个问题:很明显需要去掉B(aar)—>C(aar)的依赖。这里有两种常用的方法:直接从pom中删除依赖元数据(karaoke用的)。修改B对C的依赖类型为compileOnly,但是如果B使用C的资源打包aar,会报错。第二个问题:卡拉OK的做法是要求名称一致,只有在开发修改代码的时候才更改版本。第三个问题:因为这种模式在正常开发中也会存在,所以处理版本冲突有几种方式:开发时使用高版本覆盖所有参与构建的版本。修改B—>share:1.2.0依赖类型为compileOnly,解除传递依赖。如果一定要使用动态版本号+,并且低于参与构建的版本,可以提取白名单,从pom中删除依赖,使其依赖于app的主模块(卡拉OK采用它)。B在发布aar时,不在pom中保留任何关于三方的依赖元数据,app在编译时依赖它。解决传递依赖问题,有传递性、强制性、严格依赖等共同特征。卡拉OK很少使用这些功能。考虑到透明开发和保留原始代码的需要,我们采用直接修改pom文件依赖的方式来去除传递依赖。从上面的问题不难看出,唯一标识=自身内容+依赖图,所以在计算md5的时候,我们还需要包含依赖关系。我什么时候可以获得依赖关系图?Gradle构建生命周期分为3个步骤:1.初始化Gradle支持单项目和多项目构建。在初始化阶段,Gradle确定将参与构建的项目并为每个项目创建一个Project实例。2.配置在此阶段,将配置项目对象。执行作为构建一部分的所有项目的构建脚本。3.执行Gradle确定要在配置阶段创建和配置的任务子集。该子集由任务名称参数和传递给gradle命令的当前目录确定。Gradle然后执行每个选定的任务。很明显build.gradle是在配置阶段执行的。生成依赖图后,我们可以在项目评估回调(afterEvaluate)中分析并完成我们的操作。K歌的app模块依赖了全世界所有的库模块。编译时,应用程序首先接收评估回调。只要此时修改app的依赖图,就可以阻断后续其他库模块的配置过程。这个时候librarymodule还没有评估完成,没有依赖图是无法计算md5的。只能在library模块的build.gradle文件中手动解析依赖配置。K歌部分配置直接声明在dependencies中,部分提取到统一管理的versionConfigs.gradle中。需要注意的是,每个项目评估完成后修改依赖图是不安全的,Gradle会阻止。1.5.最终流程构建项目时,每个项目的build.gradle在配置阶段都会被执行,依赖关系会在其中确定,项目评估后(afterEvaluate)会收到通知。分析配置中本地项目类型的依赖(DefaultProjectDependency),计算项目的md5,计算的内容就是上面提到的aar内容,同时项目的依赖关系也应该作为md5计算的范围。md5计算出来后,根据maven库的寻址规则拼接成路径,检查远程maven仓库中是否存在aar。如果aar存在,将本地项目的依赖类型改为远程aar依赖(DefaultExternalModuleDependency)。
