Mobile Development 17 min read

Migrating from the Deprecated Transform API to AsmClassVisitorFactory in Android Gradle Plugin 7.x

This article explains why the Transform API is deprecated in AGP 7.0, introduces the new AsmClassVisitorFactory‑based API, compares code examples, discusses performance benefits and drawbacks, and provides guidance for migrating Android Gradle plugins to the newer variant and artifact system.

Rare Earth Juejin Tech Community
Rare Earth Juejin Tech Community
Rare Earth Juejin Tech Community
Migrating from the Deprecated Transform API to AsmClassVisitorFactory in Android Gradle Plugin 7.x

Background

Our project currently uses AGP 4.0.2, which is five major versions behind the latest AGP 7.2.1. Gradle upgrades are inevitable, and although the AGP 7.0 upgrade was paused last year, we still need to learn the new features.

What is Transform

Transform API allows third‑party plugins to manipulate compiled class files before they are converted to dex. It abstracts away task generation and execution, letting developers focus on class processing.

Work timing : operates between the Class → Dex stage.

Processing objects : compiled class files, standard resources, local and remote JAR/AAR dependencies.

Transform task : each Transform corresponds to a Task with defined inputs and outputs.

Transform chain : TaskManager links TransformTasks so that the output of one becomes the input of the next.

Transform usage scenarios

Custom processing of compiled class files.

Reading compiled class files for analysis without modification.

Transform replacement API

The Transform API is deprecated because it is difficult to combine with other Gradle features, leading to performance issues. The replacement APIs live inside the androidComponents {} block and are provided from AGP 7.2 onward.

Example code

“Friendship tip: the following code is dense; readers with code‑intensity phobia can skip to comments.”
class ExamplePlugin : Plugin
{
    override fun apply(project: Project) {
        val androidComponents = project.extensions.getByType(AndroidComponentsExtension::class.java)
        androidComponents.onVariants { variant ->
            // Add instrumentation per variant
            println("variant.name--${variant.name}")
            variant.instrumentation.transformClassesWith(
                ExampleClassVisitorFactory::class.java,
                InstrumentationScope.ALL
            ) {
                it.writeToStdout.set(true)
            }
            variant.instrumentation.setAsmFramesComputationMode(FramesComputationMode.COPY_FRAMES)
        }
    }

    interface ExampleParams : InstrumentationParameters {
        @get:Input
        val writeToStdout: Property
}

    abstract class ExampleClassVisitorFactory : AsmClassVisitorFactory
{
        override fun createClassVisitor(classContext: ClassContext, nextClassVisitor: ClassVisitor): ClassVisitor {
            return if (parameters.get().writeToStdout.get()) {
                TraceClassVisitor(nextClassVisitor, PrintWriter(System.out))
            } else {
                TraceClassVisitor(nextClassVisitor, PrintWriter(File("trace_out")))
            }
        }

        override fun isInstrumentable(classData: ClassData): Boolean {
            return classData.className.startsWith("com.wuba.instrument")
        }
    }
}

Officially, Transform Action is a possible alternative, but the demo shows that Google recommends AsmClassVisitorFactory.

AsmClassVisitorFactory

A factory to create class visitor objects to instrument classes.
The implementation must be an abstract class leaving parameters and instrumentationContext unimplemented, with an empty constructor.

Using AsmClassVisitorFactory can improve compilation speed by about 18% and reduce code size roughly fivefold.

interface AsmClassVisitorFactory
: Serializable {
    @get:Nested
    val parameters: Property
@get:Nested
    val instrumentationContext: InstrumentationContext

    fun createClassVisitor(classContext: ClassContext, nextClassVisitor: ClassVisitor): ClassVisitor
    fun isInstrumentable(classData: ClassData): Boolean
}
interface ClassData {
    /** Fully qualified name of the class. */
    val className: String
    /** List of the annotations the class has. */
    val classAnnotations: List
/** List of all interfaces implemented by the class or its super‑classes. */
    val interfaces: List
/** List of all super‑classes of the class. */
    val superClasses: List
}

ClassData is not part of ASM but provides enough information for most use‑cases.

New Extension

Since AGP 7.0, a new AndroidComponentsExtension replaces the old AppExtension . Custom transforms must now be registered on this extension, taking variant awareness into account.

TransformClassesWithAsmTask

This task maintains a list of AsmClassVisitorFactory instances and processes class files in a single I/O pass, delegating to a chain of ClassVisitor objects and finally writing with ClassWriter . This reduces I/O and explains the performance gain.

abstract val visitorsList: ListProperty
>
abstract val inputClassesDir: ConfigurableFileCollection
abstract val inputJarsDir: DirectoryProperty
abstract val inputJarsWithIdentity: JarsClasspathInputsWithIdentity
abstract val runtimeClasspath: ConfigurableFileCollection
abstract val bootClasspath: ConfigurableFileCollection
abstract val classesOutputDir: DirectoryProperty
abstract val jarsOutputDir: DirectoryProperty

Compared with the old Transform stream model, the new task eliminates the need for repeated I/O between transforms.

Advantages of AsmClassVisitorFactory

No need to write complex incremental build logic.

Significant build‑performance improvement.

Disadvantages of AsmClassVisitorFactory

Less flexible than the old Transform API; tied to ASM and cannot use Javassist or AspectJ.

Higher learning curve for ASM.

Stability of the new API

AsmClassVisitorFactory is stable in AGP 7.2.1 and can be used in production. AGP 8.0 will remove the old Transform API entirely.

AGP internal changes

From AGP 3.5.3 to 7.2.1, system transforms have gradually been replaced by tasks. By AGP 7.2.1, only custom transforms remain, and they are expected to disappear in AGP 8.0.

public void createPostCompilationTasks(@NonNull final VariantScope variantScope) {
    // External transforms
    List
customTransforms = extension.getTransforms();
    // System transforms added here in older versions
    // ...
}

New Variant API

The new Variant and Artifact APIs expose a more stable way to interact with build artifacts. Example code shows how to register a task and wire its output to a variant’s artifact collection.

abstract class ToyPlugin : Plugin
{
    override fun apply(project: Project) {
        val androidComponents = project.extensions.getByType(AndroidComponentsExtension::class.java)
        androidComponents.onVariants { variant ->
            val taskProvider = project.tasks.register(variant.name + "AddAsset", AddAssetTask::class.java) {
                it.content.set("foo")
            }
            variant.artifacts.use(taskProvider).wireWith(AddAssetTask::outputDir).toAppendTo(MultipleArtifact.ASSETS)
        }
    }
}

Industry plugin architecture

Frameworks like Booster and ByteX isolate the Transform API, making future AGP migrations easier. However, internal plugins still rely on the old API, and migration will be painful without a unified framework.

Personal thoughts

Should we develop our own plugin framework and define standards?

Should new plugins be built on ByteX or a custom framework that isolates Transform?

Is it necessary to consolidate existing internal plugins?

References

https://juejin.cn/post/7016147287889936397 https://juejin.cn/post/7056395437544046606 https://juejin.cn/post/7098752199575994405 https://blog.csdn.net/qq_33902817/article/details/122434538 https://developer.android.com/studio/build/extend-agp?hl=zh-cn https://juejin.cn/post/7105925343680135198 https://nebulae-pan.github.io/2021/12/25/Gradle7.0Transform%E6%9B%BF%E4%BB%A3%E6%96%B9%E6%A1%88/

AndroidGradleplugin developmentTransform APIbuild performanceAsmClassVisitorFactory
Rare Earth Juejin Tech Community
Written by

Rare Earth Juejin Tech Community

Juejin, a tech community that helps developers grow.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.