Series of articles look here

Gradle gets started and builds the environment

Gradle is an introduction to meta-programming

Gradle: Groovy syntax for getting started

Gradle introduction and lifecycle

Gradle core project

How to create a Gradle plugin by yourself

What is a Transform in Gradle?

An overview of the

Google provides the Transform API starting with Android Gradle 1.5.0. The Transform API allows third parties to manipulate.class files as plug-ins during compilation of Android applications before they are packaged as dex files. We just implement a Transform that iterates through all the methods in the.class file, changes it, and replaces the source file, and we can insert code.

What can Transform do

First, we can execute a build operation, which will output the following:

Aar (androidx.arch.core: core-Runtime :2.0.0) with AarTransform > Transform Lifecycle - livedata - core. The aar (androidx lifecycle: lifecycle - livedata - core: 2.0.0) with AarTransform > Transform Lifecycle - livedata. Aar (androidx. Lifecycle: lifecycle - livedata: 2.0.0) with AarTransform > Transform interpolator. The aar (androidx. Interpolator: interpolator: 1.0.0) with AarTransform > Transform savedstate. The aar (androidx. Savedstate: savedstate: 1.0.0) with AarTransform > Transform lifecycle - viewmodel. Aar (androidx. Lifecycle: lifecycle - viewmodel: 2.1.0) with AarTransform > Transform lifecycle - runtime. Aar (androidx. Lifecycle: lifecycle - runtime: 2.1.0) with AarTransform > Transform versionedparcelable. The aar (androidx. Versionedparcelable: versionedparcelable: 1.1.0) with AarTransform > Transform cursoradapter. The aar (androidx. Cursoradapter: cursoradapter: 1.0.0) with AarTransform > Transform core. The aar (androidx. Core: the core: 1.3.2) with AarTransform > Transform customview. Aar (androidx customview: customview: 1.0.0) with AarTransformCopy the code

That is, during the build process, one Transform after another is executed. So to get back to the original question, what can Transform do, let me list some of the ones that people hear a lot, and some of the ones that are common:

  • Traceless burying point: pages can be buried without invading the code, but generally this is for relatively simple cases, complex business scenarios are difficult to deal with traceless burying point.

  • Performance monitoring: This is also common.

  • Event stabilization: Avoid multiple button clicks in a short period of time.

  • Hot fix: Insert reservation function before method to make replacement.

  • .

So when exactly does the Transform operation embed the code? Take a look at Google’s official packaging image:

The Transform phase is where the red circle is, where the.class file becomes the.dex file. Transform is a set of apis that Android provides for developers to modify class files during the transformation from class to dex during project construction. The classic applications are bytecode staking and code injection. With this API, we can do some customization based on our business needs.

The Transform using

So much has been said before, which mainly introduces what a Transform is and what it can do. So how do you use it?

Let’s add a dependency to build. Gradle:

dependencies { implementation fileTree(dir: "libs", include: ["*.jar"]) implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" //gradle sdk implementation gradleApi() / / / / groovy SDK implementation localGroovy () new implementation "com. Android. View the build: gradle: 3.3.2 rainfall distribution on 10-12"}Copy the code

Then create a new MyTransform:

/ / note that Transform the import com. There are many path android. Build. API. The Transform. Transform class MyTransform extends the Transform {@ Override String getName() { return "MyTransform" } @Override Set<QualifiedContent.ContentType> getInputTypes() { return TransformManager.CONTENT_CLASS } @Override Set<? super QualifiedContent.Scope> getScopes() { return TransformManager.SCOPE_FULL_PROJECT } @Override boolean isIncremental() { return false } }Copy the code

getName

Specifies the name of the custom Transform that returns the corresponding Task name

getInputTypes

Can see this method is to return a Set of < QualifiedContent. ContentType > collection, is actually returns the file type of the Transform to deal with. The TransformManager already provides us with the following:

    public static final Set<ContentType> CONTENT_CLASS = ImmutableSet.of(CLASSES);
    public static final Set<ContentType> CONTENT_JARS = ImmutableSet.of(CLASSES, RESOURCES);
    public static final Set<ContentType> CONTENT_RESOURCES = ImmutableSet.of(RESOURCES);
    public static final Set<ContentType> CONTENT_NATIVE_LIBS =
            ImmutableSet.of(NATIVE_LIBS);
    public static final Set<ContentType> CONTENT_DEX = ImmutableSet.of(ExtendedContentType.DEX);
    public static final Set<ContentType> CONTENT_DEX_WITH_RESOURCES =
            ImmutableSet.of(ExtendedContentType.DEX, RESOURCES); 
Copy the code

getScopes

This method returns a Set< qualifiedContent.scope >, which returns the Scope of the Transform processing. Let’s take a look at the scopes: getScopes

    /** Only the project (module) content */
    PROJECT(0x01),
    /** Only the sub-projects (other modules) */
    SUB_PROJECTS(0x04),
    /** Only the external libraries */
    EXTERNAL_LIBRARIES(0x10),
    /** Code that is being tested by the current variant, including dependencies */
    TESTED_CODE(0x20),
    /** Local or remote dependencies that are provided-only */
    PROVIDED_ONLY(0x40),
    /**
     * Only the project's local dependencies (local jars)
     *
     * @deprecated local dependencies are now processed as {@link #EXTERNAL_LIBRARIES}
     */
    @Deprecated
    PROJECT_LOCAL_DEPS(0x02),
    /**
     * Only the sub-projects's local dependencies (local jars).
     *
     * @deprecated local dependencies are now processed as {@link#EXTERNAL_LIBRARIES}
    */
    @Deprecated
    SUB_PROJECTS_LOCAL_DEPS(0x08);
Copy the code

Here are the first five.

Similarly, The TransformManager provides us with a return set of scopes as follows:

    public static final Set<ScopeType> PROJECT_ONLY = ImmutableSet.of(Scope.PROJECT);
    public static final Set<Scope> SCOPE_FULL_PROJECT =
            Sets.immutableEnumSet(
                    Scope.PROJECT,
                    Scope.SUB_PROJECTS,
                    Scope.EXTERNAL_LIBRARIES);
    public static final Set<ScopeType> SCOPE_FULL_WITH_IR_FOR_DEXING =
            new ImmutableSet.Builder<ScopeType>()
                    .addAll(SCOPE_FULL_PROJECT)
                    .add(InternalScope.MAIN_SPLIT)
                    .build();
    public static final Set<ScopeType> SCOPE_FULL_WITH_FEATURES =
            new ImmutableSet.Builder<ScopeType>()
                    .addAll(SCOPE_FULL_PROJECT)
                    .add(InternalScope.FEATURES)
                    .build();
    public static final Set<ScopeType> SCOPE_FULL_WITH_IR_AND_FEATURES =
            new ImmutableSet.Builder<ScopeType>()
                    .addAll(SCOPE_FULL_PROJECT)
                    .add(InternalScope.MAIN_SPLIT)
                    .add(InternalScope.FEATURES)
                    .build();
    public static final Set<ScopeType> SCOPE_FEATURES = ImmutableSet.of(InternalScope.FEATURES);
    public static final Set<ScopeType> SCOPE_FULL_LIBRARY_WITH_LOCAL_JARS =
            ImmutableSet.of(Scope.PROJECT, InternalScope.LOCAL_DEPS);
    public static final Set<ScopeType> SCOPE_IR_FOR_SLICING =
            ImmutableSet.of(Scope.PROJECT, Scope.SUB_PROJECTS);
Copy the code

isIncremental

If true is returned, TransformInput will contain a list of files that were modified, if false, the last modified record will be deleted and a full compilation will be performed.

transform

This is the primary method; file and JAR manipulation is done here, and code migration is done through this method. The following attributes are commonly used:

  • TransformInput: Transforms the input class files into the target bytecode files. TransformInput is an abstraction of these input files. It currently contains the DirectoryInput collection and JarInput collection.

  • DirectoryInput: All directory structures and the source files in their directories that participate in project compilation by source code.

  • JarInput: All local or remote Jar packages that participate in the compilation of a project in Jar mode.

  • TransformOutProvider: This class is used to get the output path.

use

When you’re done, we just need to add the following code to our plugin to use your own Transform.

class MyGradlePlugin implements Plugin<Project> { @Override void apply(Project project) { ... Def android = project. Extensions. GetByType (AppExtension) def classTransform = new MyTransform (project) / / register your Transform android.registerTransform(classTransform) ... }}Copy the code

conclusion

Back to the title, what is a Transform? A Transform is essentially a code that can be dynamically woven during compilation. The main purpose is decoupling. Make development more business-focused. Some data monitoring, traceless burying point and other logic to Transfrom processing.

reference

Gradle- Initial code injection Transform

For more articles, please pay attention to my official number:Code the farmers work