Booster中SPI技术应用

478 阅读4分钟

背景

Booster 的基础依赖库是 booster-gradle-plugin ,它的插件入口是:com.didiglobal.booster.gradle.BoosterPlugin,我们截取处理 com.android.application 插件的逻辑:

class BoosterPlugin : Plugin<Project> {

    override fun apply(project: Project) {
    when {
        project.plugins.hasPlugin("com.android.application") -> 
        // 0
        project.getAndroid<AppExtension>().let { 
            // 1
            android.registerTransform(BoosterTransform.newInstance(project))
            // 2
            project.afterEvaluate {
                loadVariantProcessors(project).let { processors ->
                    android.applicationVariants.forEach { variant ->
                        processors.forEach { processor ->
                            processor.process(variant)
                        }
                    }
                }
            }
        }
    }        
  • 0:
project.getAndroid<AppExtension>() 

获取当前 project 的 android ksl 块,转换为 AppExtension,也就是 app 模块中定义的那一坨 android {}:

inline fun <reified T : BaseExtension> Project.getAndroid(): T = extensions.getByName("android") as T

SPI 在集成自定义 Tramform 的应用

  • 1:
android.registerTransform(BoosterTransform.newInstance(project))

调用 AppExtension 的 registerTransform 方法将 BoosterTransform.newInstance(project) 生成的 Transform 注册进去(就是典型的使用 Transform 的方式),我们这边先裁剪一些代码,不看不同版本的兼容:

fun newInstance(project: Project): BoosterTransform = when {
    else -> BoosterTransform(project)
}

可以看到直接生成一个对象返回了,我们裁剪一些代码,这个对象继承了系统的 Transform,这样子就会在 APG 打包的过程中被执行:

open class BoosterTransform protected constructor(val project: Project) : Transform() {

    // 0
    internal val transformers = loadTransformers(project.buildscript.classLoader).sortedBy {
        it.javaClass.getAnnotation(Priority::class.java)?.value ?: 0
    }
    
    // 1
    final override fun transform(invocation: TransformInvocation) {
        BoosterTransformInvocation(invocation, this).apply {
            if (isIncremental) {
                doIncrementalTransform()
            } else {
                outputProvider?.deleteAll()
                doFullTransform()
            }
        }
    }
}
  • 0:loadTransformers(project.buildscript.classLoader) 这个方法将当前 project 对象的 buildscript 构建脚本的类加载器 classLoader 作为参数传入,猜想是需要进行类加载的动作。
internal fun loadTransformers(classLoader: ClassLoader) = newServiceLoader<Transformer>(classLoader, ClassLoader::class.java).load(classLoader)

它直接调用了 newServiceLoader 方法传入范型 com.didiglobal.booster.transform.Transformer 和类加载器 classLoader 构造 com.didiglobal.booster.gradle.ServiceLoaderFactory 类并调用 newServiceLoader(vararg types: Class<*>) 方法。

internal inline fun <reified T> newServiceLoader(classLoader: ClassLoader, vararg types: Class<*>) = ServiceLoaderFactory(classLoader, T::class.java).newServiceLoader(*types)

生成 com.didiglobal.booster.gradle.ServiceLoaderFactory 类后调用它的 load(vararg args: Any) 方法,从结果看返回一个 List。那么我们可以猜想这是一个系统 Transform ,因为前面调用系统的注册方法将 Transform 注册进去了:

classLoader.getResources("META-INF/services/${service.name}")?.asSequence()?.map(::parse)?.flatten()?.toSet()?.map { provider ->
    try {
        val providerClass = Class.forName(provider, false, classLoader)
        if (!service.isAssignableFrom(providerClass)) {
            throw ServiceConfigurationError("Provider $provider not a subtype")
        }

        try {
            providerClass.getConstructor(*types).newInstance(*args) as T
        } catch (e: NoSuchMethodException) {
            providerClass.newInstance() as T
        }
    } catch (e: ClassNotFoundException) {
        throw ServiceConfigurationError("Provider $provider not found")
    }
} ?: emptyList()

先看方法体:

try {
    val providerClass = Class.forName(provider, false, classLoader)
    if (!service.isAssignableFrom(providerClass)) {
        throw ServiceConfigurationError("Provider $provider not a subtype")
    }

    try {
        providerClass.getConstructor(*types).newInstance(*args) as T
    } catch (e: NoSuchMethodException) {
        providerClass.newInstance() as T
    }
} catch (e: ClassNotFoundException) {
    throw ServiceConfigurationError("Provider $provider not found")
}

通过解析出来的 provider 通过传入的 classLoader 进行类的加载出来,然后调用它的构造函数进行初始化,并强转为我们传入的范型 T (com.didiglobal.booster.transform.Transformer)。那么我们就知道我们需要知道我们要去哪里加载一些 com.didiglobal.booster.transform.Transformer 的类。再看看去哪里找这些类:

classLoader.getResources("META-INF/services/${service.name}")?.asSequence()?.map(::parse)?.flatten()?.toSet()?.map {}

可以看到是通过路径 "META-INF/services/${service.name}" 去查找对应的类地址的。查看了 booster-gradle-plugin 库并没有发现这个目录,可是我们在构建文件 build.gradle 中发现了依赖:

kapt 'com.google.auto.service:auto-service:1.0'

这是谷歌提供的 SPI 库,所有标记注解 com.google.auto.service.AutoService 的类都会帮我们在 classpath 中生成对应的 META-INF 配置文件,就可以加载了。通过查找 com.didiglobal.booster.transform.Transformer 的子类我们发现这两个实现类。loadTransformers(project.buildscript.classLoader) 方法就分析完了。

@AutoService(Transformer::class)
class AsmTransformer : Transformer {
}

@AutoService(Transformer::class)
class JavassistTransformer : Transformer {
}
  • 1:执行 transform
final override fun transform(invocation: TransformInvocation) {
    BoosterTransformInvocation(invocation, this).apply {
        doFullTransform()
    }
}

加载了所有自定义 Tramform 之后, BoosterTransform 的 transform 方法就会在编译过程中被调用,这里构造了 BoosterTransformInvocation ,我们裁剪一些代码,调用了 doFullTransform() 方法。

internal fun doFullTransform() = doTransform(this::transformFully)

我们裁剪一些代码:

private fun doTransform(block: (ExecutorService, Set<File>) -> Iterable<Future<*>>) {
    // 0
    val executor = Executors.newFixedThreadPool(NCPU)

    // 1
    this.onPreTransform()
    
    // 2
    val outOfDate = this.lookAhead(executor).onEach {
        project.logger.info("✨ ${it.canonicalPath} OUT-OF-DATE ")
    }

    try {
        // 3
        block(executor, outOfDate).forEach {
            it.get()
        }
    } finally {
        executor.shutdown()
        executor.awaitTermination(1, TimeUnit.HOURS)
    }
    
    // 4
    this.onPostTransform()
}
  • 0:val executor = Executors.newFixedThreadPool(NCPU) 为传入的 block 参数构建执行器。

  • 1:this.onPreTransform()

private fun onPreTransform() {
    transform.transformers.forEach {
        it.onPreTransform(this)
    }
}

这个 transform 就是我们传入的参数,它包含了通过 com.google.auto.service.AutoService 注解的自定义 Tramform。也就是说,这边会进行一个钩子操作,分发系统的生命周期给自定义的 Tramform。

  • 2:加载需要处理的文件
val outOfDate = this.lookAhead(executor).onEach {
    project.logger.info("✨ ${it.canonicalPath} OUT-OF-DATE ")
}

向前看以确定需要转换哪些输入。

  • 3:执行传入的代码块 block

  • 4:this.onPostTransform() 这个 transform 就是我们传入的参数,它包含了通过 com.google.auto.service.AutoService 注解的自定义 Tramform。也就是说,这边会进行一个钩子操作,分发系统的生命周期给自定义的 Tramform。

主要逻辑在执行传入的代码块 block,也就是 transformFully 方法:

private fun transformFully(executor: ExecutorService, outOfDate: Set<File>) = this.inputs.map {
    it.jarInputs + it.directoryInputs
}.flatten().map { input ->
    executor.submit {
        val format = if (input is DirectoryInput) Format.DIRECTORY else Format.JAR
        outputProvider?.let { provider ->
            project.logger.info("Transforming ${input.file}")
            input.transform(provider.getContentLocation(input.name, input.contentTypes, input.scopes, format))
        }
    }
}

简单说一下流程,就是根据在执行系统的 Tramfrom 的时候传入的 inputs 中过滤 jar 包和文件夹。通过传入的执行器执行任务,在执行的任务中,处理每个输入的 input,input.transform(provider.getContentLocation(input.name, input.contentTypes, input.scopes, format)):

private fun QualifiedContent.transform(output: File) {
    outputs += output // 将输出进行收集
    this.file.transform(output) { bytecode ->
        bytecode.transform()
    }
}

private fun ByteArray.transform(): ByteArray {
    return transform.transformers.fold(this) { bytes, transformer ->
        transformer.transform(this@BoosterTransformInvocation, bytes)
    }
}

可以看到,对于每个输入输出,都会调用这个 transform 就是我们传入的参数,它包含了通过 com.google.auto.service.AutoService 注解的自定义 Tramform。这样子就将自定义的 Tramfrom 集成到系统 Tramsform 的流程中了。

SPI 在集成自定义 Task 的应用

  • 2:
project.afterEvaluate {
    // 0
    loadVariantProcessors(project).let { processors ->
        android.applicationVariants.forEach { variant ->
            processors.forEach { processor ->
                // 1
                processor.process(variant)
            }
        }
    }
}
  • 0:loadVariantProcessors(project)
internal fun loadVariantProcessors(project: Project) = newServiceLoader<VariantProcessor>(project.buildscript.classLoader, Project::class.java).load(project)

这里的流程跟 Tramform 是一样的,不同的只是传入的范型是 com.didiglobal.booster.task.spi.VariantProcessor,参数是 Project。我们可以猜想到有一大堆集成自 VariantProcessor 的子类都被标注了 com.google.auto.service.AutoService 注解。

image.png

挑一个来看:

@AutoService(VariantProcessor::class)
@Priority(1)
class PngquantCompressionVariantProcessor : VariantProcessor {}
  • 1:processor.process(variant) 执行所有的 Task 的 process(variant: BaseVariant) 方法。

SPI 在兼容不同版本的 Gradle 的应用

BoosterTransform 作为 booster 中的抽象 Transform ,在初始化方法中有:

fun newInstance(project: Project): BoosterTransform = when {
    GTE_V3_4 -> BoosterTransformV34(project)
    else -> BoosterTransform(project)
}

这边大概可以想到是根据这个方法进行版本兼容的。定义了一堆东西:

image.png

看看 GTE_V3_4 -> BoosterTransformV34(project),首先 GTE_V3_4 是怎么生成的:

val AGP: AGPInterface by lazy {
    val factory = FACTORIES.firstOrNull {
        it.revision.major == REVISION.major && it.revision.minor == REVISION.minor
    } ?: FACTORIES.firstOrNull {
        it.revision.major == REVISION.major
    } ?: FACTORIES.first()
    factory.newAGPInterface()
}

private val FACTORIES: List<AGPInterfaceFactory> by lazy {
    // 通过 classloader 加载 META-INF/services/ 文件夹中的 com.didiglobal.booster.gradle.AGPInterfaceFactory 类实例
    ServiceLoader.load(AGPInterfaceFactory::class.java, AGPInterface::class.java.classLoader)
            .sortedByDescending(AGPInterfaceFactory::revision)
            .toList()
}

AGP 在被引用的时候就会创建 FACTORIES,我们很熟悉的又看到了类似的加载方法,可以看到这些子类都会被加载:

image.png

随便看一个子类,也是标注了 com.google.auto.service.AutoService 注解:

@AutoService(AGPInterfaceFactory::class)
class V70Factory : AGPInterfaceFactory {
    override val revision: Revision = Revision(7, 0, 0)
    override fun newAGPInterface(): AGPInterface = V70
}

在不同的子类中定义了不同的版本和不同版本的实现类。然后在遍历所有实现类的时候跟当前的 gradle 版本 REVISION.major && REVISION.minor 进行对比,来选择哪个实现类。

image.png