跳至主要內容

适配 AGP 8.0

guodongAndroid大约 3 分钟Kotlinandroidkotlinkcp

前言

AGP 即 android-gradle-plugin

随着 AGP 的更新,AGP 1.5 版本增加的 Transform API 在 7.0 版本时被标记为废弃,已经在 8.0 版本被移除,基于 Transform API 的插件不得不进行适配。

Android Gradle 插件 API 更新 | Android Studio | Android Developersopen in new window

Instrumentation API

在 AGP 7.0 版本官方提供了 Instrumentation API 来代替 Transform API,前者相对于后者性能上有所提升,开发者也不再需要处理增量编译且实现字节码修改更为简单。

com.android.build.api.instrumentation | Android Developersopen in new window

注册 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
    }
}
  1. LibraryAndroidComponentsExtension 扩展的 onVariants 函数中可以为不同的变体注册不同的Instrumentation,

  2. 通过 transformClassesWith 函数注册 Instrumentation,

  3. 要注册的 Instrumentation,

  4. 指定扫描范围:InstrumentationScope.PROJECT 或者 InstrumentationScope.ALL

  5. 注册的 Instrumentation 需要的参数,

  6. setAsmFramesComputationMode 函数设置不同的栈帧计算模式,类似设置 ASM 中 ClassWriterflag 参数:0、COMPUTE_MAXS 或者 COMPUTE_FRAMES

以下是 Extension 与 Plugin Id 的对应关系:

ExtensionPlugin Id
AndroidComponentsExtension-
ApplicationAndroidComponentsExtensioncom.android.application
LibraryAndroidComponentsExtensioncom.android.library
TestAndroidComponentsExtensioncom.android.test
DynamicFeatureAndroidComponentsExtensioncom.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 的方式性能上有所提升。

Maskopen in new window0.0.8 版本已支持 AGP 7.4.0 - 8.0 版本。