Learn more about Gradle

Gradle is a configuration file. Gradle is a configuration file. Gradle is a configuration file. To learn more about Gradle, we need to think of it as a programming framework, and we need to understand its apis and use them to accomplish certain tasks.

Gradle workflow lifecycle

  • Initialization phase: The settings.gradle file is executed to determine how many projects the project contains

  • The goal of the Configration phase is to parse build.gradle in each project. Parse build.gradle in each subdirectory to load plug-ins, load dependencies, load tasks, and execute scripts, respectively

  • This is the Execution phase

  • Lifecycle official documentation description

Gradle programming model

  • Gradle is written in Groovy and Groovy is based on Java, so when Gradle executes Groovy scripts, it parses them into Java objects, which have three basic types

  • Gradle object: When we run the Gradle XXX command, Gradle constructs a Gradle object from the default configuration script. There is only one object in the entire execution. The data type of a Gradle object is Gradle.

  • Project object: Each build.gradle is converted to a Project object

  • Settings object: Each settings.gradle is converted to a Settings object

Every Gradle Script implements this Script interface. This interface defines a number of properties and methods that can be used in scripts

  • Official Document Description

Project

  • Project the API documentation

  • In the Gradle programming model described in the previous section, each build. Gradle file is converted into a Project object. There is a one-to-one relationship between Project and build. Gradle files. In Gradle terminology, a Project object corresponds to a Build Script

  • Each Project contains many tasks, which are corresponding plug-ins. It can also be said that the number of tasks in a Project is actually determined by the number of plug-ins

So in build.gradle, you need to load the plugin, load the dependencies, and set the properties

Load the plug-in

  • A Project corresponds to build.gradle, and the plug-in load calls the apply function of Project, which is actually defined in the PluginAware interface of Project implementation, as described in the official documentation below

  • Build. Gradle: build. Gradle: build
// If project is compiling Android APP, then load the apply plugin: 'com.android.application' // If project is compiling Library, apply plugin: 'com.android. Library 'Copy the code
  • The apply function can also load gradle files. For example, to create unified dependency management, you can create config.gradle, write the dependency configuration and version number to it, and then use it in build.gradle

Gradle files must be in the same directory as gradle files. Otherwise, file path + file name format is required to apply

AbsolutePath +'/config.gradle' apply from: rootproject.getrootdir ().absolutePath+'/config.gradle'Copy the code
  • The config. Gradle file

Closures, also called closures, are a very important data type or concept in Groovy; == This represents an executable piece of code ==

Ext {// Android = [compileSdkVersion: 28, buildToolsVersion: "28.0.0", minSdkVersion: 21, targetSdkVersion: 28, versionCode: 7, versionName: "1.0.6", renderscriptTargetApi: 21] version = [supportLibraryVersion: "28.0.0", smartrefreshVersion: "1.0-alpha-25", okhttpVersion: "3.12.0" retrofitVersion: "2.3.0" glideVersion: "4.8.0" daggerVersion: "2.22.1" butterknifeVersion: "", fragmentationVersion:" ",] dependencies = [//base" "com.android.support:appcompat-v7:${version["supportLibraryVersion"]}", "cardview-v7" : "com.android.support:cardview-v7:${version["supportLibraryVersion"]}", "support-v4" : "com.android.support:support-v4:${version["supportLibraryVersion"]}", "design" : "com.android.support:design:${version["supportLibraryVersion"]}", "recyclerview" : "com.android.support:recyclerview-v7:${version["supportLibraryVersion"]}", "constraint-layout" : "Com. Android. Support. The constraint, the constraint - layout: 1.1.3",...] }Copy the code
  • Gradle defines dependencies using config.gradle in build.gradle also requires a combination of file path and name
dependencies {
    implementation rootProject.ext.dependencies["appcompat-v7"]
}
Copy the code

Task

  • First post the Api document address of the Task as usual

  • A Task is a data type in Gradle that represents some work to be performed or done. Different plug-ins

Different tasks can be added. Each Task needs to be associated with a Project

  • You can define tasks in build.gradle as follows
Task myTask Task myTask {// configure closure} // e.g. Project. Task ("hello1"){doLast {println("Hello from the GreetingPlugin")}} task myType << {task action} // note, The << symbol is short for doLast. // You can specify the Type Task myTask(Type: SomeType) //eg: task myTask(type:Copy) // Create a Copy task is a Copy task task myTask(type: SomeType) {// Configure closure}Copy the code
  • A Task can have several actions. Each Task has two functions, doFirst and doLast, to add the actions that need to be executed first and the actions that need to be executed last. An Action is a closure.

  • When using task myTask {XXX}, the parenthesis is a closure. After creating this Task, Gradle executes closure’s logical Task before returning it to the user

  • Task myType << {Task action}, can specify the Type, expressed by Type: name. Gradle provides several generic tasks, the most common of which is the Copy Task. Copy is a class in Gradle. When we :task myTask(type:Copy), the task created is a Copy task.

// Task copyDocs(type: Copy) {from 'SRC /main/doc' // Copy from the SRC /main/doc directory into 'build/target/doc' // to the build/target/doc directory} // See the official documentation for more examplesCopy the code
  • The Copy documents

  • The previous part is just to understand some concepts of Task. For more details, you need to check the official documentation.

Transform

Many third-party frameworks rely on custom plug-ins, such as Ali routing framework ARouter, and its implementation includes the use of Transform. Understanding and using Transform is the basis for understanding third-party frameworks.

What is the Transform

  • Starting with 1.5.0-beta1, the Gradle plugin includes a Transform API that allows third-party plug-ins to manipulate compiled class files before converting them to dex files.

  • Transform is a Task. When you compile an Android project, the project code will be compiled into a.class file through compileJava Task. The Transform will receive the.class file. And the Transform will be executed after the Task compileJava, which means you can do some custom operations before the Android project generates the dex.

  • Transform dependency introduction

Implementation 'com. Android. Tools. Build: gradle: 4.0.0'Copy the code

Transform API

  • Transform Gradle, an abstract class in the Transform Gradle plug-in, has four abstract methods that must be implemented, as shown below
@SuppressWarnings("MethodMayBeStatic") public abstract class Transform { @NonNull public abstract String getName(); @NonNull public abstract Set<ContentType> getInputTypes(); @NonNull public abstract Set<? super Scope> getScopes(); /** * Returns whether the Transform can perform incremental work. then the TransformInput may contain a list of changed/removed/added files, unless * something else triggers a non incremental run. */ public abstract boolean isIncremental(); . }Copy the code

Transform Task name setting

  • Abstract method getName() : Implementing this method returns the name of the plug-in Task

Task handles input file types

  • Abstract method getInputTypes() : Returns the MutableSet < QualifiedContent. ContentType > set type, type of CLASSES for retrieving only. Class files, the RESOURCES type represents the retrieval Java standard resource files.
/** * implements ContentType {/** *. Class file */ CLASSES(0x01), /** Standard Java RESOURCES */ RESOURCES(0x02); private final int value; DefaultContentType(int value) { this.value = value; } @Override public int getValue() { return value; }}Copy the code

Task handles input file ranges

  • The abstract method getScopes() : Returns a set of MutableSet types. Scope is an enumerated type with the following meanings
Enum Scope implements ScopeType {/** only implements ScopeType */ PROJECT(0x01), /** only implements ScopeType */ SUB_PROJECTS(0x04), /** Only external libraries */ EXTERNAL_LIBRARIES(0x10), /** Code tested by current variables, Includes dependencies */ TESTED_CODE(0x20), /** Local or remote dependencies provided only */ PROVIDED_ONLY(0x40),...... }Copy the code
  • Several combinations of enumerations have been defined for us in the TransformManager class
public static final Set<ScopeType> PROJECT_ONLY = ImmutableSet.of(Scope.PROJECT);

public static final Set<ScopeType> SCOPE_FULL_PROJECT =
            ImmutableSet.of(Scope.PROJECT, Scope.SUB_PROJECTS, Scope.EXTERNAL_LIBRARIES);
            
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_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_FULL_PROJECT_WITH_LOCAL_JARS =
            new ImmutableSet.Builder<ScopeType>()
                    .addAll(SCOPE_FULL_PROJECT)
                    .add(InternalScope.LOCAL_DEPS)
                    .build();
Copy the code

The Transform method

  • Transform is an empty implementation whose core inputs are encapsulated in a TransformInvocation object
override fun transform(transformInvocation: TransformInvocation) {
    
}
Copy the code
TransformInvocation
  • Its member parameters are as follows
public interface TransformInvocation { /** * Returns the inputs/outputs of the transform. * @return the inputs/outputs of the transform. */ @NonNull Collection<TransformInput> getInputs(); /** * Returns the output provider allowing to create content. * @return he output provider allowing to create content. */ @Nullable TransformOutputProvider getOutputProvider(); . }Copy the code
  • Inputs are TransformInput. Output objects are customized inputs, obtained through the TransformOutputProvider to the output directory, and copied to the output directory
/**
 * The input to a Transform.
 * <p>
 * It is mostly composed of a list of {@link JarInput} and a list of {@link DirectoryInput}.
 */
public interface TransformInput {

    /**
     * Returns a collection of {@link JarInput}.
     */
    @NonNull
    Collection<JarInput> getJarInputs();

    /**
     * Returns a collection of {@link DirectoryInput}.
     */
    @NonNull
    Collection<DirectoryInput> getDirectoryInputs();
}
Copy the code
  • TransformInput defines the transform method to two types of inputs: jarInputs and directoryInputs

  • The following example prints the input JAR package and.class file names separately

override fun transform(transformInvocation: TransformInvocation) {println (" the transform method calls ") / / for input file collection val transformInputs = TransformInvocation. The inputs TransformInputs. ForEach {transformInput - > / / jar file processing transformInput jarInputs. ForEach {jarInput - > val file = jarInput.file println("find jar input: "+ file name)} / / processing / / directoryInputs represents a source file with source way involved in the project to compile all the directory structure and its source code files in the directory transformInput. DirectoryInputs. ForEach { DirectoryInput - > / / iterate through all the files and folders to find the class directoryInput end file. The file. The walkTopDown (). The filter {it. IsFile}. The filter { ForEach {file -> println("find class file: ${file.name}")}}}}Copy the code
  • The results

Register the Transform in the plug-in

  • Gradle’s Project will load the plugin, so we need to register the Transform Task in the plugin as shown below. Customize ASMLifecycleTransform and register it in your custom plug-in
/** * @Description: * @author maoqitian * @date 2020/11/13 0013 17:01 */ class MainPlugin :Plugin<Project> {Override fun apply(project: Project) {println("====== custom MainPlugin load ===========") // Register to execute custom Transform task val asmTransform = project.extensions.getByType(AppExtension::class.java) println("=======registerTransform ASMLifecycleTransform ==========") val transform = ASMLifecycleTransform() asmTransform.registerTransform(transform) } }Copy the code

More articles

  • Github.com/maoqitian/N…

The last

  • In this article, you can read the official documentation for Gradle plugins.