The book continues:

  • Gradle Pit Climbing Guide – Introduction
  • Gradle Pit Crawl Guide – Grammar and concepts first

Gradle: Hello World! Groovy: Hello World! Groovy: Hello World! At this point, we must have a clear understanding of Gradle. To learn Gradle is to learn a new language and then apply its API. Well, it’s not too difficult. Even if there are some things that we don’t understand, as long as we master the general context, we can learn all the difficult points through later learning

How to Understand scripts

“Gradle” is a script file. Ioo: Groovy is a Java extension based dynamic scripting language to ioo what the scripting was. You hear about scripts a lot, but a lot of people don’t really know what a script is, it’s pretty simple, okay

In traditional Java programs, we declare class at the beginning of every Java file, and all code must be written in class {… }, otherwise the compiler does not recognize. The class we write is called a class, and the class must specify the starting point for code execution, usually the main() function

class Person {
    int name
    int age
    
    public static void main(String[] args) {... }}Copy the code

A script is an extension of the class concept, a simpler file:

  • Script files do not start with Class{… }, you can write code directly. Of course, you can still write class{… }, but do not need to write at the beginning, of course, write Groovy also can recognize
  • End with file.xxThe file type represents the script file type, for example.gradleThis is a Groovy script, and the compiler compiles the script file based on this representation
  • The script file does not execute the starting location or method of the code execution; the script defaults to the first line of execution
def to(x, y) {
    x + y
}

class Person {
    def name
    def age   
}

Person person1 = new Person()
Person person2 = new Person()
person1.name = "AA"
person2.name = "BB"

to(111.222)
Copy the code

Scripting is a more flexible and easier way to write code, but it actually needs to be compiled by the compiler into the corresponding class file to execute. Gradle scripts, for example, are compiled into Java objects for execution on the JVM. In fact, it depends on the compiler. Some script files can be directly executed by the compiler without being compiled into the object of the corresponding language

Of Task

Official document –> with Google browser translation plug-in or can see, recommend you or the official document over, this experience is necessary

Let me tell you the plot

Tasks are the basic unit of Gradle execution. Create a Task based on the configuration of Gradle, Setting, and Project. Create a Task based on the configuration of Gradle. Directed acyclic graph. Let’s look at this graph

Final Gradle will, in accordance with the figure on the Task of each link order execution, each Task is to complete a specific function, according to the sequence of Task graph is performed again, the construction of the whole project to complete the Task ┗ | ` O ‘| ┛ ao ~ ~

What is a Task

In Gradle, each Project to be compiled is called a Project. Each Project is built with a set of tasks. For example, an Android APK compilation might include: Java source compiled tasks, resource compiled tasks, JNI compiled tasks, Lint checked tasks, packaged apK-generated tasks, signed tasks, etc. How many tasks a Project contains depends on the plug-in specified by the compiled script. What is a plug-in? Plug-ins are things that define tasks and concretely execute them

Task can be regarded as a Task, which can be compilation of Java code, compilation of C/C++ code, compilation of resource file to generate the corresponding R file, packaging to generate JAR, AAR library file, signature, obturation, image compression, packaging to generate APK file, package/resource distribution, etc. Gradle can now build Java, Android, Web, lib projects

In the figure below, all these tasks are Task by Task. Each Task is to achieve a certain goal, which can be understood as a step. Finally, the whole project construction is completed by executing step by step in sequence

A Task is a class of tasks that actually correspond to an object. A Task is composed of numerous actions, which represent functions and methods. Each Task is an execution diagram composed of a bunch of actions in order, just as we call a series of methods logically in the main function of Class

Create a Task

task hello{
    println "hello world"
}

task(hello2){
    println "hello world2"
}

task ('hello3') {println "hello world3"
}
Copy the code

Run the Task

  • Direct command executiongradle TaskName –> gradle hello
  • Perform a task in a subprojectgradle :MoudleName:TaskName –> gradle :app:clean

The attribute of the Task

These attributes can be written in () when creating tasks

task test(name:"test".group:"AA") {dolast{... }}Copy the code

Task Extra Attributes

task asss{
    ext {
        name = "AA"
        age = 18
    }

    doLast {
        println("name = "+ name)
        println("age = "+ age)
    }
}
Copy the code

The Task of the action

Actions are not unique within a Task, but a set of actions to which you can add various actions. Note that actions are executed in a sequential order, which is specified. These action receptions are closures, see the declaration in Task below

 // Add Action to the Action queue header
 Task doFirst(Action<? super Task> action);
 Task doFirst(Closure action);

 // Add Action to the end of the Action queue
 Task doLast(Action<? super Task> action);
 Task doLast(Closure action);

 // Delete all actions
 Task deleteAllActions(a);
Copy the code

doFirst{… }, doLast {… } accept all closures

  • Actions added by doFirst are executed first
  • The action of the task itself is executed in the middle. This action cannot be added outside the task, but can be written when you customize the task
  • The action added by doLast is executed last

A task can also act like an object. It can also write code, such as printing AA, but this code can only be executed in the configuration phase, whereas the actions of a task are executed in the runtime phase. If you don’t know about the configuration phase and execution phase, see below

task speak{
    println("This is AA!")
    doFirst {
        println("This is doFirst!")}doLast {
        println("This is doLast!")
    }
}

speak.doFirst {
	println("This is doFirst!")}Copy the code

Actions can be added in the order in which they are added, such as doLast

task speak{
    println("This is AA!")
    doFirst {
        println("This is doFirst!")}doLast {
        println("This is doLast1... !")
    }
}

speak.configure {
    doLast {
        println 'his is doLast2... '
    }
}
speak.doLast{
        println 'his is doLast3... '
}
Copy the code

< < shorthand

task task1 << {
    println "I am task1 -"
}
Copy the code

<< instead of doLast{… }, this thing is simple simple, but no reading, the most hate this kind of thing, actually add a lot of trouble ~

Dynamically creating tasks

4.times { counter ->
    task "task$counter" {
        doLast {
            println "I'm task number $counter"}}}Copy the code

This settingtaskTask0. DependsOn task2, task3Copy the code

Tasks share data

Two tasks use and manipulate the same data. The ext global variable is used to create a Task that uses a variable in a.gradle script. However, in 6.6.1 I found that variables can be used directly, which should be official optimization

ext {
    name = "AAA"
}

def age = 18

task s1 {
    doLast {
        age = 12
        rootProject.ext.name = "BBB"
        println("This is s1...")}}task s2 {
    doLast {
        println("age --> " + age)
        println("name --> " + rootProject.ext.name)
        println("This is s2...")}}Copy the code

Task dependent on

DependsOn (Task) specifies the order in which tasks are executed.

  • A.dependsOn B --> Execute B before executing A
  • A.mustRunAfter B --> Execute A and B at the same time. Execute B first and then EXECUTE A. If the execution relationship is not established, an error is reported
  • A.shouldRunAfter B --> Is the same as mustRunAfter, but the execution relationship is not established

1. dependsOn –>

task s1{
    doLast {
        println("This is s1...")}}task s2{
    doLast {
        println("This is s2...")}} s1. DependsOn s2 -- -- -- -- -- -- -- -- -- - or -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -task s2{
    dependsOn s1
    doLast {
        println("This is s2...")}} -----------dependsOn has not been declaredtaskTo add""----------------

task s1{
    dependsOn "s2"
    doLast {
        println("This is s2...")}}Copy the code

2. mustRunAfter –>

task s1{
    doLast {
        println("This is s1...")}}task s2{
    doLast {
        println("This is s2...")
    }
}

s1.mustRunAfter s2
Copy the code

The custom Task

In Gradle, tasks inherit from DefaultTask. Our custom tasks also inherit from DefaultTask. We write our own methods and add @taskAction to indicate that this method is the action of the Task

class MyTask extends DefaultTask {

    String message = "mytask..."

    @TaskAction
    def ss1() {
        println("This is MyTask --> action1!")
    }

    @TaskAction
    def ss2() {
        println("This is MyTask --> action2!")}}task speak(type: MyTask) {
    println("This is AA!")
    doFirst {
        println("This is doFirst!")}doLast {
        println("This is doLast!")}}Copy the code

Default Task

Gradle provides many tasks to use by default, such as copy and delete

1. copy

Copy Copy files

task speak (type: Copy) {... }Copy the code
// Data source directory, multiple directories
public AbstractCopyTask from(Object... sourcePaths)  

// Target directory, single
public AbstractCopyTask into(Object destDir) 

// Filter file contains
public AbstractCopyTask include(String... includes)

// Filter files out
public AbstractCopyTask exclude(String... excludes)

// Rename, old name new name
public AbstractCopyTask rename(String sourceRegEx, String replaceWith)

// Delete the file Project interface
boolean delete(Object... paths);
Copy the code
Copy images: Multiple data sources -->task copyImage(type: Copy) {
    from 'C:\\Users\\yiba_zyj\\Desktop\\gradle\\copy' , 
         'C:\\Users\\yiba_zyj\\Desktop\\gradle\\copy'
    into 'C:\\Users\\yiba_zyj\\Desktop'
}
Copy the code
Copy files: Filter files, rename files -->task copyImage(type: Copy) {
    from 'C:\\Users\\yiba_zyj\\Desktop\\gradle\\copy'
    into 'C:\\Users\\yiba_zyj\\Desktop'
    include "*.jpg"
    exclude "image1.jpg"
    rename("image2.jpg"."123.jpg")}Copy the code

2. Delete

Delete the file

Delete files on desktop -->task deleteFile(type: Delete) {
    // Delete system desktop delete
    delete "C:\\Users\\yiba_zyj\\Desktop\\gradle\\delete"
}
Copy the code

Set the default Task Task

Setting this means that we do not call the task in the script and the task will be executed

defaultTasks 'clean'.'run'

task clean {
    doLast {
        println 'Default Cleaning! '}}task run {
    doLast {
        println 'Default Running! '}}task other {
    doLast {
        println "I'm not a default task!"}}Copy the code
> gradle -q

Default Cleaning!
Default Running!
Copy the code

External dependencies are used in Task

Buildscript {… } import remote repository and dependency path, import can be used

import org.apache.commons.codec.binary.Base64

buildscript {
	// Import to the repository
    repositories {
        mavenCentral()
    }
    // Add a specific dependency
    dependencies {
        classpath group: 'commons-codec', name: 'commons-codec', version: '1.2'}}task encode {
    doLast {
        def byte[] encodedString = new Base64().encode('hello world\n'.getBytes())
        println new String(encodedString)
    }
}
Copy the code

The Plugin plug-in

Maybe it’s not quite the right explanation, but that’s basically what it means

First remember the word Plugin: Plugin

Understand what plug-ins are

We write projects using a lot of third-party code, and Gradle as a build tool needs to be able to manage third-party code, or package it to produce the final output. Because this extra code is added to the final product, Gradle’s ability to manage third-party code is a must

Some of this third-party code is involved in business code and some of it is involved in project construction:

  • The third party code involved in building the project is called --> plug-ins
  • The third party code that participates in the code logic is called a dependency

Plug-ins, or dependencies, are essentially a bunch of classes, functions, or apis. The difference is where they are used

Gradle is a framework, and as a framework, it is responsible for defining processes and rules. The specific compilation work is done by plug-in. For example, compiling Java has Java plug-ins, compiling Groovy has Groovy plug-ins, compiling Android APP has Android APP plug-ins, compiling Android Library has Android Library plug-ins. Each Gradle Project to be compiled is a Project. A specific compilation process is defined and executed by a Task. How many tasks a Project contains depends on the plug-in specified by the compiled script. What is a plug-in? Plug-ins are things that define tasks and concretely execute them

The hierarchy of plug-ins

The overall architecture of Gradle plug-ins looks like this

  • At the bottom level is the Gradle framework, which provides basic services such as task dependencies, common tasks such as building directed acyclic graphs, and building specifications
  • In the middle is the Android Gradle Plugin developed by the Google team, which provides the output of many tasks and variants related to the packaging of Android projects
  • At the bottom is a developer custom Plugin that hooks official tasks with their own tasks and uses Transform for compile-time code injection

What plug-ins do:

Gradle itself is a general-purpose build system that does not know whether the project or code you are compiling is Java or C. Java code needs Javac to compile.java to.class, while C code needs GCC to compile.c to.o

In the Task section, we said that the entire construction process is composed of Task tasks. The construction of a project involves many steps: code compilation, resource compilation, dependency library packaging, etc. Based on the idea of extracting common and specific abstract templates, Gradle is used as a universal project building tool. Gradle encapsulates the common parts of the project building, and the corresponding build plug-ins for each project take over the different parts of the project building according to the characteristics and differences

So if coder were to do this on our own, no one would be able to remember all of these differences no, who has no time to memorize this, so the plugin was born. The apply plugin: ‘com.android.application’ is a build plugin for android projects. Library ‘com.android.library’ is a build plug-in for an Android dependent project. These plug-ins encapsulate the entire build process of the corresponding project, that is, they have a Task directed acyclic graph defined inside

We just need to introduce the plugin in the.gradle build script and configure a few properties according to the project situation to build the project. This is the purpose of the Java plug-in

Of course, in order to complete a certain function, we can also write the code as a plug-in stored in the remote repository for everyone to use ~

Plug-in naming rules

Plug-ins are typically stored in a remote repository, and there are fixed rules for finding specific plug-ins from the repository

  1. Common warehouse rules –> Group :name:version

    • Example: com. Walfud: myplugin: 1.2.3
    • Repositories like Maven, Google, JCenter, and JVY refer to plug-ins through this naming rule
  2. Gradle warehouse rules – > id: id. Gradle. Plugin: version

    • Example: com. Android. Application: com. Android. Application. Gradle. Plugin: 1.2.3
    • Ioo to the gradle warehouse to be different from others, looking at the egg pain, really no difficulty to create difficulties ヾ(≧O≦)
  3. Gradle switches to The Google repository.

    • Gradle warehouse: com. Android. Application: com. Android. Application. Gradle. Plugin: 1.2.3
    • Google warehouse: com. Android. Tools. Build: gradle: 1.2.3
pluginManagement {
    repositories{... google() } resolutionStrategy { eachPlugin {if (requested.id.namespace == "com.android") {
                useModule("com.android.tools.build:gradle:${requested.version}")}}}}Copy the code

This is written on the official document, but I tried to report an error, maybe it is changed, my Gradle version: 6.6.1

Finally, the introduction to the Gradle repository corresponds to plug-in naming (honestly, I don’t know what this is for ヾ(´∀ ‘o)+)

Gradle built-in plugins

Gradle has many built-in plug-ins, such as Java plugin and build-scan plugin. To introduce these plug-ins, we can use Gradle built-in functions directly, without specifying id and version

Built-in plugins like this:

repositories {
	google()
	jcenter()
}
Copy the code

Google () and jcenter() are obviously functions (○ ‘3’ ○). We see the group:name:version of the plugin.

Gradle already wrote this for us (>▽<). Clicking on Google () and JCenter () will get you to a class RepositoryHandler. Built-in means that Gradle is already written and we can use it directly

Gradle’s built-in plugins are all repositories of plugins. Of course, the repository itself is also a plug-in

The import plug-in

To insert the plug-in, we import the repository, which declares which repository to look for the plug-in and remote dependencies from, and then import the plug-in. As long as we declare the imported plug-in in the build.gradle build script of the root project, it is available to all subprojects

  • The root projectbuild.gradleThe import plug-in
buildscript {
    repositories {
        google()   
    }
    dependencies {
        classpath 'com. Android. Tools. Build: gradle: 3.5.0'}}allprojects {
    repositories {
        google()
        jcenter()
    }
}
Copy the code
  • Subprojects use plug-ins
app build.gradle --> 

apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'android { .... defaultConfig { .... } buildTypes { .... }}dependencies{... }Copy the code

Several of them {… } closure:

  • buildscript {… } –> declare the repository and add plugin-specific closures
  • repositories {… } –> Set up the remote repository
  • dependencies {… } –> Add the plugin address, of course you can also add remote dependencies used in setting.gradle script
  • Although the plugin is imported in, it is a matter of whether the subproject is used or not. If not, there is no need to package it in, so take the initiative to declare it

Apple just looks up the Apple method in the plugin and calls it. Gradle instantiates this class for you and calls its methods

repositories {… } This closure takes other parameters, such as the remote repository account, password, and so on

repositories {
    maven {
        url = uri(...)
        
        username = "joe"
        password = "secret"}}Copy the code

dependencies {… } this is the address of the plugin used, of course, the plugin can also write the remote dependency used in setting.gradle script. Gradle script dependencies{… } If you write the address of the plugin, you will not be able to use the plugin with Apply later

buildscript {
  repositories {
    mavenCentral()
    google()
  }
  dependencies {
    classpath 'com. Jakewharton: butterknife - gradle - plugin: 10.2.3'
  }
}

apply plugin: 'com.android.library'
apply plugin: 'com.jakewharton.butterknife'
Copy the code

Configuring plug-in properties

This is an important point to understand (○ ‘3’ ○)

As mentioned earlier, plug-ins are collections of third-party code that participate in the construction of a project. This code not only participates in the construction of the project, but also requires the user to enter some configuration parameters, which are also passed in as closures

The plugin has internal methods that receive the parameters configured in the build.gradle script. The closures that pass configuration parameters to plug-ins are called DSL snippet, and the classic scenario is android {… } the

app build.gradle –>

apply plugin: 'com.android.application'

android {
    compileSdkVersion(28) defaultConfig { .... }}Copy the code

android {… } This DSL is not a Gradle configuration, but comes from the com.android.application plugin. If you understand this and look at build.gradle later, you can think of a DSL as a plugin’s init method, which is essentially what it is

The DELEGATE + closure is used to implement the DSL configuration block in the plug-in

This has already been said in the grammar phase and is posted here to emphasize its importance

The idea is simple, each {… } closures should have a corresponding data Bean to store data, when appropriate. Delegate

  1. Closure definition
def android = {
	compileSdkVersion 25
	buildToolsVersion "25.0.2"
    
    // This corresponds to the corresponding method
	defaultConfig {
		minSdkVersion 15
		targetSdkVersion 25
		versionCode 1
		versionName "1.0"}}Copy the code
  1. Preparing the data Bean
class Android {
    int mCompileSdkVersion
    String mBuildToolsVersion
    BefaultConfig mBefaultConfig

    Android() {
        this.mBefaultConfig = new BefaultConfig()
    }

    void defaultConfig(Closure closure) {
        closure.setDelegate(mProductFlavor)
        closure.setResolveStrategy(Closure.DELEGATE_FIRST)
        closure.call()}}class BefaultConfig {
    int mVersionCode
    String mVersionName
    int mMinSdkVersion
    int mTargetSdkVersion
}
Copy the code
  1. .delegateData binding
Android bean = new Android()
android.delegate = bean
android.call(a)Copy the code

Gradle build process

Gradle script execution –> means that Gradle compiles the. Gradle script files to create the corresponding objects and tasks

Gradle’s build process is universal, and any project built by Gradle follows this process

Gradle builds are divided into three stages. Each stage has its own responsibility and each stage completes a part of the task. The results of the previous stage are the prerequisite for the next stage:

  • Initialization –> Initialization phase. Run in orderinit.gradle -> settings.gradleScript to generate Gradle, Setting, Project objects
  • Configuration–> Compile phase, also called configuration phase. Run in orderroot build.gradle -> Subprojects build. GradleScript to generate Task execution flowchart
  • Execution–> Execution phase. Run each Task in the order of Task execution diagram, complete step by step, and generate the final APK file

Look at the picture:

Throughout the build process, there are officially hook hook functions on some nodes so that we can add our own logic during the build phase and affect the build process. Linsener: Linsener: Linsener: Linsener: Linsener: Linsener

I’m going to focus on init.gradle and Settings. gradle

Initialization phase

You can do a lot of things with Gradle objects as the parameters of Gradle objects are global properties of Gradle objects built during Initialization

  • For example, you can getGradle Home, Gradle User Homedirectory
  • Adding a Global Listener
  • Add Settings, dependencies, and so on to all projects

Two Groovy scripts are run in sequence during the Initialization phase:

  • The Init Script:Create the built-in Gradle object
  • Setting the Script:Create the built-in object Setting and Project object corresponding to each module

The Configuration phase

After Initialization, all build.gradle scripts are executed in the Configuration phase. The build script of the root project is executed first, and then the build scripts of the sub-projects are executed one by one based on the dependencies between the sub-projects

Gradle has root projects and subprojects. Gradle has a Project object for every Project. There is only one root project, and there can be multiple sub-projects. Even the shell project of app Module in Android, which we often say, is actually a sub-project. By default, the root project of Gradle is empty and has no content. Gradle has real meaning for the project

Gradle project must have a. Gradle build script. In the Configuration stage, all project build scripts are executed from the start of the project until all sub-project build scripts are executed. Create the corresponding Project object, in the next step, 1 Project object corresponds to 1 Project, rootProject represents the rootProject, Project represents the subproject

The DSL configuration blocks in gradle scripts are all methods of Project objects

Then, according to the Configuration in the script, the final product of the Configuration stage is generated: Task directed acyclic graph, which is executed in the next stage. The purpose of the Configuration phase is to calculate the logic and flow required in the entire construction process according to the script Configuration, which can be understood as the process of dynamically generating code. With dynamically generated code, there’s something to execute later.

Execution stage

The Execution phase of the task is executed as a result of the task Execution diagram calculated in the Configuration phase

The directed acyclic graph can be obtained by using the gradle.gettaskGraph () method

When after the completion of directed acyclic graph to build by whenReady or addTaskExecutionGraphListener (TaskExecutionGraphListener) to receive the corresponding notification

gradle.getTaskGraph().addTaskExecutionGraphListener(new TaskExecutionGraphListener() {
    @Override
    void graphPopulated(TaskExecutionGraph graph) {}})Copy the code

This is the Android build process, where each step can be thought of as a Task(in reality, each step has multiple tasks)

If you look at the above image carefully, it is not difficult to see that the entire build process is made up of these sub-steps, which steps are started first, and which steps are executed last, depending on what plug-ins you import into your project. As mentioned earlier, Gradle is a universal build tool that sets rules detailing how each project should be built. The plug-in is responsible for the specific content and flow of the process. O ( ̄ε ̄*)

Let’s print the rebuild process to get a sense of what the build process is composed of tasks. Tasks are subdivided steps

Executing tasks: [clean, :libs:assembleDebug, :app:assembleDebug] in project /Users/zbzbgo/worksaplce/flutter_app4/MyApplication Starting Gradle Daemon... Gradle Daemon started in 1 s 423 ms > Configure project : This is AA! index == class java.lang.Integer > Task :clean > Task :libs:clean > Task :libs:preBuild UP-TO-DATE > Task :libs:preDebugBuild UP-TO-DATE > Task :libs:compileDebugAidl NO-SOURCE > Task :app:clean > Task :app:preBuild UP-TO-DATE  > Task :app:preDebugBuild UP-TO-DATE > Task :app:compileDebugAidl NO-SOURCE > Task :libs:mergeDebugJniLibFolders > Task  :libs:compileDebugRenderscript NO-SOURCE > Task :app:createDebugCompatibleScreenManifests > Task :app:extractDeepLinksDebug > Task :app:mergeDebugShaders > Task :app:javaPreCompileDebug > Task :app:compileDebugShaders  NO-SOURCE > Task :app:generateDebugAssets UP-TO-DATE > Task :app:processDebugJavaRes NO-SOURCE > Task :libs:packageDebugResources > Task :libs:parseDebugLocalResources > Task :libs:mergeDebugNativeLibs > Task :libs:stripDebugDebugSymbols NO-SOURCE > Task :libs:copyDebugJniLibsProjectAndLocalJars > Task :libs:mergeDebugShaders >  Task :libs:compileDebugShaders NO-SOURCE > Task :libs:packageDebugAssets > Task :libs:packageDebugRenderscript NO-SOURCE > Task :app:generateDebugResources > Task :libs:compileDebugLibraryResources > Task :libs:copyDebugJniLibsProjectOnly > Task :libs:processDebugManifest > Task :libs:javaPreCompileDebug > Task :libs:compileDebugKotlin > Task :app:processDebugManifest > Task :app:mergeDebugAssets > Task :libs:compileDebugJavaWithJavac > Task :libs:compileDebugSources > Task :libs:assembleDebug > Task :app:mergeDebugJniLibFolders > Task :app:mergeDebugResources > Task :app:mergeLibDexDebug > Task :app:processDebugResources > Task :app:mergeExtDexDebug > Task :app:compileDebugKotlin > Task :app:compileDebugJavaWithJavac > Task :app:compileDebugSources > Task :app:mergeDebugNativeLibs > Task :app:dexBuilderDebug > Task :app:mergeProjectDexDebug > Task :app:mergeDebugJavaResource > Task :app:packageDebug > Task  :app:assembleDebug Deprecated Gradle features were used in this build, Making it incompatible with Gradle 7.0. Use '--warning-mode all' to show the individual deprecation warnings. See https://docs.gradle.org/6.6.1/userguide/command_line_interface.html#sec:command_line_warnings BUILD SUCCESSFUL in 1 m 6 s 51 actionable tasks: 49 executed, 2 up-to-dateCopy the code

The above section is too much to read, you can see the effect, you can try to see the output log on the Build console

Diagrammatic construction process

The content of the above build process is from the official documentation. Here is something different. The information comes from:

  • An in-depth exploration of Gradle Automatic Construction technology (5, Gradle plug-in Architecture implementation Principle analysis – part 1)
  • Gradle principle animation explanation

Thanks to the continuous output of the predecessors, the following is a little more detailed than the above, which is very helpful for you to understand the whole process. In fact, you can understand it by looking at this picture carefully

When we start executing Gradle build commands, we go through the following steps:

  1. First, initialize Gradle to build the framework itself
  2. Then, wrap the command-line arguments and send them to DefaultGradleLauncher
  3. Finally, the Gradle build lifecycle in DefaultGradleLauncher is triggered and the standard build process begins

Gradle has three processes: Wrapper, Client and Deamon

  • The Client process manages each specific build task and shuts down after the build. It parses various builds, initializes configurations, creates Gradle objects, passes these parameters to the Deamon process, and receives Gradle build logs sent back by the Deamon process
  • The Deamon process is responsible for the specific build. The Deamon process is a daemon that does not shut down the process at the end of the build, but is cached and reused
  • The Wrapper process is responsible for managing Gradle versions

1) Wrapper first

The first program to execute after the build starts is from the Wrapper. The AS developer must confirm that the Gradle environment is OK before the build can start

Call gradle/wrapper/gradle – wrapper. Jar/GradleWrapperMain. The main () to start the wrapper

public static void main(String[] args) throws Exception {

        // Dynamically build a WrapperExecutor instance according to gradle zip address in gradle-wrapper.properties
        WrapperExecutor wrapperExecutor = WrapperExecutor.forWrapperPropertiesFile(propertiesFile);
        // 2. Run the execute method to start the task
        wrapperExecutor.execute(args, new Install(logger, new Download(logger, "gradlew"."0"), new PathAssembler(gradleUserHome)), new BootstrapMainStarter());
}

public void execute(String[] args, Install install, BootstrapMainStarter bootstrapMainStarter) throws Exception {
        // download the gradle Wrapper dependencies
        File gradleHome = install.createDist(this.config);
        // 2. Start the Gradle build process here
        bootstrapMainStarter.start(args, gradleHome);
    }
Copy the code

2) The BootstrapMainStarter object controls the entire build process

From BootstrapMainStarter. Start () is the official start of the Gradle build process, would in turn perform BootstrapMainStarter four methods:

  • getLoadedSettings
  • getConfiguredBuild
  • executeTasks
  • finishBuild

I don’t want to go into details, but you can just look at the picture, which is very Nice

The BootstrapMainStarter object initializes the Client process, and then executes the global initialization script init.gradle to initialize the global Gradle object. Note that global parameters configured in gradle.properties are resolved before executing settings.gradle script

The back of nothing to say, interested to see the source code, no time to see the map on the line

3) The most detailed Android plug-in packaging flow chart

Init. Gradle script

Gradle initialization scripts work on all Gradle projects on the machine and are the first scripts executed during each build. Executing this script creates Gradle objects

Because the init.gradle initialization script works on all projects on the machine, the script is not written in a specific project, but is added to gradle’s configuration environment

Init. gradle script configures the path

Add the init.build file in either of the following paths:

  • GRADLE_USER_HOME/init.gradle
  • GRADLE_USER_HOME/init.d/init.gradle
  • If you are managing Gradle as a local file, this is your local Gradle address

Print some data in the initialization script

println("init...")
println("gradleHomeDir:${gradle.gradleHomeDir}")
println("gradleUserHomeDir:${gradle.gradleUserHomeDir}")
println("gradleVersion:${gradle.gradleVersion}")
println("startParameter:${gradle.startParameter}")
Copy the code

initscript {… }

The script has a DSL configuration block: initscript {… }, you can add remote dependencies, and the configuration block is run first in the script

Observe initscript {… } Running order

println("init...")

initscript{
    println("initscript...")}Copy the code

initscript {… } import third-party library and use, the following from the official documentation

import org.apache.commons.math.fraction.Fraction

initscript {
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath 'org.apache.com mons: the Commons - math: 2.0'}}println Fraction.ONE_FIFTH.multiply(2)
Copy the code

Use initialization scripts to implement global configuration

initscript {
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath 'org.apache.com mons: the Commons - math: 2.0'}}Copy the code

Initialization scripts are used to do global configuration:

  • Set up enterprise-wide configurations, such as where can I find custom plug-ins
  • Set the properties based on the current environment, such as the developer's computer and the continuous integration server
  • Provide personal information about users, such as repository or database authentication credentials, needed for the build
  • Define machine-specific details, such as where the JDK is installed
  • Register to build listeners. External tools that want to listen for Gradle events may find this useful
  • Register the build logger. You might want to customize how Gradle logs the events it generates

Settings. Gradle script

During initialization, the init.gradle script is executed, followed by the Settings. gradle script. Settings. gradle scripts manage dependencies between subprojects and determine which projects are involved in the build

Note:

  1. All a Gradle project needs is a Settings. Gradle script
  2. Settings. gradle script, once executed, creates the Setting object
  3. At this time, the build.gradle script of the Project object has not been run. The content of the Project object is still empty, but it does not delay us to set it at this time

The include function

The include function is used to specify which subprojects will participate in the build, and all include projects will participate in the package. The default path is relative to the project path, so include ‘:app’ will do, but if we make changes to the module project directory, we’ll have to set the file path ourselves

// Universal Settings
include ':app'.':baseComponents'.':baselib'.':commonRepositroy'.':player'

// Manual setting -- Mode 1: relative path
project(':baseComponents').projectDir = new File('components/baseComponents')
project(':baseComponents').projectDir = new File('.. /components/baseComponents')

// Manual setting -- Mode 2: absolute path
project(':baseComponents').projectDir = new File(rootProject.projectDir.path,'/components/baseComponents')

// Import module subprojects of other projects
include "speech"
project(":speech").projectDir = new File(".. /OtherApp/speech")
Copy the code

The project path is shown below:

Performing global Configuration

Init. gradle scripts, after all, operate on all projects on the machine, which is a bit wide and sometimes not necessary. If you just want to set up this project, it’s ok to put these Settings in settings.gradle. The build.gradle build script for the subproject has not yet been executed, so we can do whatever we want

Build. Gradle: buildScript {… } move to setting.gradle script. The catch here is that neither the blogs nor the official documentation mention buildScript {… }, let you write directly {… }

Buildscript {… buildscript {… } DSL configuration blocks declare dependent plug-ins. This is an outdated way of writing. Gradle’s latest versions move this part to setting. Gradle

setting.gradle –>

include ':app'
rootProject.name = "My Application"

buildscript {
    ext.kotlin_version = "1.4.10"
    repositories {
        google()
        jcenter()
    }
    dependencies {
        classpath "Com. Android. Tools. Build: gradle: 4.0.1." "
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"}}Copy the code

PluginManagement doesn’t seem to work

As long as I write pluginManagement here, I can’t get it to compile, there should be some changes, but I don’t see the explanation in the official document

pluginManagement {
    resolutionStrategy {
        eachPlugin {
            if (requested.id.id == "com.dorongold.task-tree") {
                useVersion("1.4")}}}}Copy the code

root build.gradle

The.gradle build scripts for subprojects are written with DSL configuration blocks for plugins such as Android {… }, this will be discussed in the next article. This article describes the.gradle build script in the root project

The build script in the root project does nothing but two things:

  • One is the introduction of plug-ins
  • The other is to configure global properties

Settings. gradle has moved the plugin configuration DSL from the root project script to settings.gradle

Function configuration is the same as init.gradle script

root build.gradle –>

ext.kotlin_version = "1.4.10"
buildscript {
    ext.kotlin_version = "1.4.10"
    repositories {
        google()
        jcenter()
    }
    dependencies {
        classpath "Com. Android. Tools. Build: gradle: 4.0.1." "
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"}}allprojects {
    repositories {
        google()
        jcenter()
    }
}
Copy the code

Relationships between projects

In either build script, The parent module can be obtained by subprojects. All the child modules can be obtained by parent. If there is no direct relationship between the two modules, You can refer to it through findProject. By default, all modules are evaluated alphabetically. If a module depends on another module, the evaluationDependsOn will be used to specify the evaluationDependsOn, so that the evaluated module will be evaluated firstCopy the code

Gradle. The properties file

Gradle. Properties is used to write global parameters, just like static. Gradle was used extensively in the early days. Each parameter in this file can be referenced by a.gradle script anywhere, even if it is a subproject. The gradle.properties file is located in the root directory of the project

In addition, VM option parameters such as stack size can be configured in the gradle.properties file

gradle.properties –>

Org.gradle. jvmargs = -XMx2g -xx: MaxMetaspaceSize =512M - XX: + HeapDumpOnOutOfMemoryError - Dfile. Encoding = utf-88
Copy the code

It is also very easy to use. Just call the name of the parameter and make sure that the name is not the same as the name of the built-in parameter

gradle.properties –>

nameAA = "AAA"
age = 123
Copy the code

app build.gradle –>

println("name:" + nameAA)
println("age:" + age)
Copy the code