适配 AGP 8.0
前言
AGP 即 android-gradle-plugin
。
随着 AGP 的更新,AGP 1.5 版本增加的 Transform API 在 7.0 版本时被标记为废弃,已经在 8.0 版本被移除,基于 Transform API 的插件不得不进行适配。
Android Gradle 插件 API 更新 | Android Studio | Android Developers
Instrumentation API
在 AGP 7.0 版本官方提供了 Instrumentation API 来代替 Transform API,前者相对于后者性能上有所提升,开发者也不再需要处理增量编译且实现字节码修改更为简单。
注册 Instrumentation
在 Transform API 中是注册在 LibraryExtension
中的,而 Instrumentation API 是注册在 LibraryAndroidComponentsExtension
中:
val extension = target.extensions.findByType(LibraryAndroidComponentsExtension::class.java)
if (extension == null) {
target.logger.error("Only support [AndroidLibrary].")
return
}
extension.onVariants { variant -> // 1
with(variant.instrumentation) {
transformClassesWith( // 2
MaskTransformFactory::class.java, // 3
InstrumentationScope.PROJECT // 4
) { parameter -> // 5
}
setAsmFramesComputationMode(FramesComputationMode.COPY_FRAMES) // 6
}
}
在
LibraryAndroidComponentsExtension
扩展的onVariants
函数中可以为不同的变体注册不同的Instrumentation,通过
transformClassesWith
函数注册 Instrumentation,要注册的 Instrumentation,
指定扫描范围:
InstrumentationScope.PROJECT
或者InstrumentationScope.ALL
,注册的 Instrumentation 需要的参数,
setAsmFramesComputationMode
函数设置不同的栈帧计算模式,类似设置 ASM 中ClassWriter
的flag
参数:0、COMPUTE_MAXS
或者COMPUTE_FRAMES
,
以下是 Extension 与 Plugin Id 的对应关系:
Extension | Plugin Id |
---|---|
AndroidComponentsExtension | - |
ApplicationAndroidComponentsExtension | com.android.application |
LibraryAndroidComponentsExtension | com.android.library |
TestAndroidComponentsExtension | com.android.test |
DynamicFeatureAndroidComponentsExtension | com.android.dynamic-feature |
由于 Mask 只应用于 [Android Library],所以我们需要注册在 LibraryAndroidComponentsExtension
中。
实现 Instrumentation
要实现 Instrumentation,需要实现 AsmClassVisitorFactory
接口并为其指定所需参数的泛型:
abstract class MaskTransformFactory : AsmClassVisitorFactory<InstrumentationParameters.None> {
}
由于 Mask 不需要参数,所以所需参数的泛型为:InstrumentationParameters.None
。
如果需要参数,则需要继承
InstrumentationParameters
接口:interface ParametersImpl : InstrumentationParameters { @get:Input val intValue: Property<Int> @get:Internal val listOfStrings: ListProperty<String> }
其中的属性必须使用 Gradle 的输入注解标识并且 参数必须是可序列化的类型,自定义类需实现
Serializable
接口。完成上述操作后,为
AsmClassVisitorFactory
接口指定所需参数的泛型:abstract class MaskTransformFactory : AsmClassVisitorFactory<ParametersImpl> { }
注册 Instrumentation 时就可以注入相应参数:
extension.onVariants { variant -> with(variant.instrumentation) { transformClassesWith( MaskTransformFactory::class.java, InstrumentationScope.PROJECT ) { parameter -> + params.intValue.set(1) + params.listOfStrings.set(listOf("a", "b")) } } }
在实现 AsmClassVisitorFactory
的接口方法之前,有必要先了解下 ClassData
接口:
interface ClassData {
/**
* Fully qualified name of the class.
*/
val className: String
/**
* List of the annotations the class has.
*/
val classAnnotations: List<String>
/**
* List of all the interfaces that this class or a superclass of this class implements.
*/
val interfaces: List<String>
/**
* List of all the super classes that this class or a super class of this class extends.
*/
val superClasses: List<String>
}
ClassData
接口定义了当前要处理类的一些信息,这有助于我们判断是否需要处理当前类。
然后在 MaskTransformFactory
中我们实现以下两个方法即可:
// 根据 ClassData 判断是否需要处理当前类,若是需要处理则调用 `createClassVisitor()` 函数
override fun isInstrumentable(classData: ClassData): Boolean {
return true
}
// 创建自己的 ClassVisitor
override fun createClassVisitor(
classContext: ClassContext,
nextClassVisitor: ClassVisitor
): ClassVisitor {
val cv = CheckClassAdapter(nextClassVisitor)
val asmApi = instrumentationContext.apiVersion.get()
return MaskClassNode(asmApi, cv)
}
得益于 ASM 的访问者设计模式,我们可以在 createClassVisitor
中创建自己的 ClassVisitor
链,从而达到多个处理逻辑的解耦。
本文适配工作基于 AGP 7.4.2 版本,需要 Java 11 才可编译,所以在
build.gradle.kts
中需要指定 Java 版本:java { sourceCompatibility = JavaVersion.VERSION_11 targetCompatibility = JavaVersion.VERSION_11 } tasks.withType<KotlinCompile> { kotlinOptions.jvmTarget = "11" }
总结
AsmClassVisitorFactory
相比 Transform API
,使用起来更加简单,不需要手动处理增量逻辑,可以专注于字节码操作。同时 AsmClassVisitorFactory
通过减少IO
的方式性能上有所提升。
现 Mask 的
0.0.8
版本已支持 AGP 7.4.0 - 8.0 版本。