背景
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 注解。
挑一个来看:
@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)
}
这边大概可以想到是根据这个方法进行版本兼容的。定义了一堆东西:
看看 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,我们很熟悉的又看到了类似的加载方法,可以看到这些子类都会被加载:
随便看一个子类,也是标注了 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 进行对比,来选择哪个实现类。