可能是 AGP8 编译最快的方案

2,349 阅读2分钟

补充:
开源仓库地址:github.com/HuolalaTech…

原文请访问开源实验室:kymjs.com/code/2024/1…

背景

AGP8 的变更应该很多人都知道了,移除了Transform API,所以很多 class 操作类的插件代码都需要改了。

TheRouter在开发的时候就支持了AGP8,使用的也是Gradle提供的标准 API。

详细可见官方示例仓库:github.com/android/gra…

project.plugins.withType(AppPlugin::class.java) {
    // Queries for the extension set by the Android Application plugin.
    // This is the second of two entry points into the Android Gradle plugin
    val androidComponents =
        project.extensions.getByType(ApplicationAndroidComponentsExtension::class.java)
    // Registers a callback to be called, when a new variant is configured
    androidComponents.onVariants { variant ->
        val taskProvider = project.tasks.register<ModifyClassesTask>("${variant.name}ModifyClasses")

        // Register modify classes task
        variant.artifacts.forScope(ScopedArtifacts.Scope.PROJECT)
            .use(taskProvider)
            .toTransform(
                ScopedArtifact.CLASSES,
                ModifyClassesTask::allJars,
                ModifyClassesTask::allDirectories,
                ModifyClassesTask::output
            )
    }
}



问题

这里使用的是toTransform()这个方法替代老版本的 API,这么做以后有个问题,就是编译速度会非常非常慢,尤其是工程里代码量越大,编译速度越慢,原因我们先看看这个方法的定义。

/**
 * Transform the current version of the [type] artifact into a new version. The order in which
 * the transforms are applied is directly set by the order of this method call. First come,
 * first served, last one provides the final version of the artifacts.
 *
 * @param type the [ScopedArtifact] to transform.
 * @param inputJars lambda that returns a [ListProperty] or [RegularFile] that will be used to
 * set all incoming files for this artifact type.
 * @param inputDirectories lambda that returns a [ListProperty] or [Directory] that will be used
 * to set all incoming directories for this artifact type.
 * @param into lambda that returns the [Property] used by the [Task] to save the transformed
 * element. The [Property] value will be automatically set by the Android Gradle Plugin and its
 * location should not be considered part of the API and can change in the future.
 */
fun toTransform(
    type: ScopedArtifact,
    inputJars: (T) -> ListProperty<RegularFile>,
    inputDirectories: (T) -> ListProperty<Directory>,
    into: (T) -> RegularFileProperty)

重点在这一句:last one provides the final version of the artifacts。这个方法是整个构建最后一步执行,会提供一个最终的输出,并且输出类型是一个 RegularFileProperty 他只能输出一个单独的 File。

这也就意味着你需要把 前期构建的所有产物,包括全部的 jar 依赖、源码编译后的 class 依赖,都在这个Task中聚合到一个产物内返回,这就是耗时的原因。

并且由于需要将所有的 jar 和 class 聚合到一个 jar 内,所以也没办法使用增量编译,这就又进一步拖慢了编译速度。

而对应的老版本操作是,拿到所有的 jar 和 class,需要字节码操作的就做操作,不需要的直接复制一遍到输出路径就可以。很显然,新版本使用的方案还不如旧版本,把所有的class聚合成一个新的jar是很耗时的,而且没办法做增量操作。