Recently I plan to learn how to use Gradle in Android. As a result, the articles published by Baidu are all introductory articles. Then I found the gradle website tutorials and masturbated to them.

Summary of Gradle.

Gradle is a project automation build tool based on Apache Ant and Apache Maven concepts. Instead of traditional XML, it uses a Groovy-based domain-specific language to declare project Settings. Gradle is the management of the project. It helps us with dependencies, packaging, deployment, distribution, and differential management of various channels.

Gradle advantage:

  1. One of the latest, most powerful build tools, use it to push the bar higher
  2. Using programs instead of traditional XML configuration makes project construction more flexible
  3. Rich third-party plugins, let you use as you like
  4. What Maven and Ant can do, Gradle can do, but what Gradle can do, Maven and Ant cannot do.

Groovy is an agile jVM-based development language that combines many of the powerful features of Python, Ruby, and Smalltalk. Groovy can integrate perfectly with Java and use all of Java’s libraries. It supports new language features such as dynamic typing and closures syntactically, seamlessly integrates all existing Java class libraries, and supports both object-oriented and procedural programming

The advantage of Groovy:

  1. A more agile programming language
  2. Getting started is very easy and very powerful
  3. It can be used as either a programming language or a scripting language

When I first started, I was confused about Gradle and Groovy. I thought they were the same language. Gradle is a build tool that uses Groovy.

Preparations:

Let’s go into action.

To use Gradle, you can create a new Android project in Android Studio. Use other IDE or need to configure the environment friends, you can baidu related articles.

The article examples are built on Gradle 5.1.1.

 Task 

Everything in Gradle is based on two basic concepts: projects and tasks.

Each Gradle build consists of one or more projects. What a project represents depends on what you are doing in Gradle. For example, a project might represent a JAR library or a Web application. It might represent a distribution ZIP assembled from jars produced by other projects. Projects don’t necessarily represent things to build. It might indicate something to accomplish, such as deploying the application to staging or production. Don’t worry just yet that doesn’t seem clear. Gradle’s conventionally built support adds a more concrete definition to the project definition.

Each project consists of one or more tasks. Tasks represent some atomic work performed by the build. This might be compiling some classes, creating jars, generating Javadoc, or publishing some archives to the repository.

Now, you’ll look at building some simple tasks in a project. Later sections cover working with multiple projects and more information about working with projects and tasks.

Hello world 

Again, let’s start with Hello World. You can run a Gradle build using the following gradle commands. The gradle command looks for a file named build.gradle in the current directory. This build.gradle file is called a build script, although technically it is a build configuration script, as we’ll see later. The build script defines the project and its tasks.

To try this, create a build script named build.gradle below.

Example 1. For the first build script, go to build.gradle in your Android project and add the following code:

// build.gradle task hello { doLast { println 'Hello world! '}}Copy the code

In the project move to the included directory and execute the build script using the following command:

./gradlew -q hello // Android users use./gradlew in the root directoryCopy the code
Gradle -q hello // Use gradle for non-Android usersCopy the code

Run using the -q command-line option. This cancels Gradle’s log messages so that only the output of the task is displayed. This makes the sample output clearer. If you don’t want to, you don’t need to use this option.

The command to execute the build script is placed directly before the comment instead of being shown on a single line.

Define the task

Here you will see how to define tasks using strings as task names. There are some variations of this style that you may need to use in certain situations.

task('hello') {
    doLast {
        println "hello"
    }
}

task('copy', type: Copy) {
    from(file('srcDir'))
    into(buildDir)
}
Copy the code

The two tasks created above are Hello and copy. There is an alternative syntax for defining tasks that you might prefer to use:

tasks.create('hello') {
    doLast {
        println "hello"
    }
}

tasks.create('copy', Copy) {
    from(file('srcDir'))
    into(buildDir)
}
Copy the code

There are also two tasks created above: Hello and copy. Finally, Groovy and Kotlin DSLS have language-specific syntax:

// Using Groovy dynamic keywords

task(hello) {
    doLast {
        println "hello"
    }
}

task(copy, type: Copy) {
    from(file('srcDir'))
    into(buildDir)
}
Copy the code

Build scripts using code

Gradle build scripts give you all the power of Groovy and Kotlin. As an appetizer, look at this: Using Groovy or Kotlin in Gradle tasks:

// gradle -q upper
task upper {
    doLast {
        String someString = 'mY_nAmE'
        println "Original: $someString"
        println "Upper case: ${someString.toUpperCase()}"
    }
}
Copy the code
// gradle -q count
task count {
    doLast {
        4.times { print "$it "}}}Copy the code
 
Copy the code

Dependencies between tasks

DependsOn: Tasks can have dependencies between tasks.

// gradle -q intro task hello { doLast { println 'Hello world! ' } } task intro { dependsOn hello doLast { println "I'm Gradle" } }Copy the code

Dependent tasks can be declared later, but an error will be reported if they are not declared:

// gradle -q taskX 
task taskX {
    dependsOn 'taskY'
    doLast {
        println 'taskX'
    }
}
task taskY {
    doLast {
        println 'taskY'
    }
}
Copy the code

Task taskX depends on taskY, which is postdeclared.

You can define task dependencies in a number of ways. In Task Dependencies, you learned about defining dependencies using task names. Task names can refer to tasks in the same project as the task, or to tasks in other projects. To reference a task in another project, add the path of the project to which it belongs before the task name. Here is an example of adding dependencies from projectA:taskX to projectB:taskY:

// gradle -q taskX
project('projectA') {
    task taskX {
        dependsOn ':projectB:taskY'
        doLast {
            println 'taskX'
        }
    }
}

project('projectB') {
    task taskY {
        doLast {
            println 'taskY'
        }
    }
}
Copy the code

In this case, projectA, projectB should be changed to the name of your project. Simply put, tasks at different levels can depend on each other.

Dynamic task

Groovy or Kotlin’s capabilities can be used to define functionality beyond tasks. For example, you can also use it to create tasks on the fly.

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

Task0, Task1, task2, task3

Operations on created tasks

Once a task is created, it can be accessed through API **. For example, you can dynamically add dependencies to tasks at run time.

// gradle -q task0
4.times { counter ->
    task "task$counter" {
        doLast {
            println "I'm task number $counter"
        }
    }
}
task0.dependsOn task2, task3
Copy the code

Alternatively, you can add behavior to an existing task.

// gradle -q hello
task hello {
    doLast {
        println 'Hello Earth'
    }
}
hello.doFirst {
    println 'Hello Venus'
}
hello.configure {
    doLast {
        println 'Hello Mars'
    }
}
hello.configure {
    doLast {
        println 'Hello Jupiter'
    }
}
Copy the code

Calls to doFirst and doLast can be performed multiple times. They add actions to the beginning or end of the task action list. When a task is executed, the actions in the action list are performed in sequence.

Groovy DSL shortcut notation

There is a convenient notation for accessing existing tasks. Each task can be used as an attribute of the build script:

// gradle -q hello task hello { doLast { println 'Hello world! ' } } hello.doLast { println "Greetings from the $hello.name task." }Copy the code

In this example, by getting the name of the task, you know that this is what the task from Task Hello does. This improves the readability of your code, especially when using tasks provided by plug-ins, such as the Compile task.

Additional task attributes

You can add your own properties to the task. Add a property named myProperty and set an initial value for ext.myProperty. Properties can be read and set just like predefined task properties.

// gradle -q printTaskProperties
task myTask {
    ext.myProperty = "myValue"
}

task printTaskProperties {
    doLast {
        println myTask.myProperty
    }
}
Copy the code

The default task

Gradle allows you to define one or more default tasks if no other tasks are specified.

// gradle -q 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

This is equivalent to running Gradle Clean Run. In a multi-project build, each subproject can have its own specific default task. If the child project does not specify a default task, the parent project’s default task (if defined) is used

Configure through DAG

As we will describe in more detail later (see Build Lifecycle), Gradle has a configuration phase and an execution phase. After the configuration phase, Gradle knows all the tasks that should be executed. Gradle gives you an opportunity to take advantage of this information. One use case is to check whether the publish task is in the task to be executed. From there, you can assign different values to certain variables.

In the following example, the execution of the Distribution and release tasks results in different values for the version variable.

// gradle -q distribution // gradle -q release task distribution { doLast { println "We build the zip with version=$version" } } task release { dependsOn 'distribution' doLast { println 'We release now' } } Gradle. TaskGraph. WhenReady {taskGraph - > if (taskGraph. HasTask (" : the release ")) {version = '1.0'} else {version = '1.0 the SNAPSHOT'}}Copy the code

You can see that executing different tasks has different results here.

Build the external dependencies of the script

If the build script needs to use external libraries, you can add them to the classpath of the script in the build script itself. You do this using the buildscript() method and pass in a block that declares the buildscript classpath.

buildscript { repositories { mavenCentral() } dependencies { classpath group: 'commons-codec', name: 'commons-codec', version: '1.2'}}Copy the code

The blocks of code in the buildScript () method form ScriptHandler instances. You can declare the build script classpath by adding dependencies to the CLASspath configuration. This is the same way you declare the Java compilation classpath. You can use any dependency type other than project dependencies.

Once the build script classpath is declared, the classes in the build script can be used just like any other class on the class path. The following example adds to the previous example and uses classes from the build script classpath.

// gradle -q encode import org.apache.commons.codec.binary.Base64 buildscript { repositories { mavenCentral() } 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 dependency is added, and then a task is created that references the classes in the dependency to encrypt the string.

Access the properties of the task

You usually need to find the tasks defined in the build file, for example, to configure them or use them for dependencies. There are many ways to do this. First, just like defining tasks, Groovy and Kotlin DSLS have language-specific syntax

task hello
task copy(type: Copy)

// Access tasks using Groovy dynamic properties on Project

println hello.name
println project.hello.name

println copy.destinationDir
println project.copy.destinationDir
Copy the code

Tasks can also be obtained through the Tasks collection.

task hello
task copy(type: Copy)

println tasks.hello.name
println tasks.named('hello').get().name

println tasks.copy.destinationDir
println tasks.named('copy').get().destinationDir
Copy the code

You can access the task from any project using the path of the task using the tasks.getbypath () method. You can call this method with getByPath() using the task name, relative path, or absolute path.

project(':projectA') {
    task hello
}

task hello

println tasks.getByPath('hello').path
println tasks.getByPath(':hello').path
println tasks.getByPath('projectA:hello').path
println tasks.getByPath(':projectA:hello').path
Copy the code

Pass the parameters to the task constructor

Instead of configuring the variable properties of a variable after the Task is created, you can pass parameter values to the constructor of the Task class. To pass the value to the Task constructor, you must use the annotated constructor @javax.inject.Inject.

class CustomTask extends DefaultTask {
    final String message
    final int number

    @Inject
    CustomTask(String message, int number) {
        this.message = message
        this.number = number
    }
}
Copy the code

You can then create a task and pass the constructor arguments at the end of the argument list.

tasks.create('myTask', CustomTask, 'hello', 42)
task myTask(type: CustomTask, constructorArgs: ['hello', 42])
Copy the code

Either way is fine. In all cases, the value passed as a constructor parameter must be non-NULL. If you try to pass a null value, Gradle will throw a NullPointerException indicating which runtime value is NULL.

Add a description to the task

You can add a description to the task. This description is displayed when gradle Tasks is executed.

// gradle tasks
task copy(type: Copy) {
   description 'Copies the resource directory to the target directory.'
   from 'resources'
   into 'target'
   include('**/*.txt', '**/*.xml', '**/*.properties')
}
Copy the code

Change the task

Sometimes you want to replace tasks. For example, if you want to swap tasks added by a Java plug-in with other types of custom tasks. You can do this using the following methods:

// gradle -q copy
task copy(type: Copy)

task copy(overwrite: true) {
    doLast {
        println('I am the new one.')
    }
}
Copy the code

When defining a new task, the overwrite property must be set to true. Otherwise, Gradle raises an exception saying that a task with that name already exists.

Skip the task

Gradle provides several ways to skip the execution of a task.

Using a predicate

You can use the onlyIf() method to attach the predicate to the task. The action of the task is performed only if the predicate is evaluated to true. You implement the predicate as a closure. The closure is passed to the task as an argument and returns true if the task should be executed; Return false if the task should be skipped. Evaluate predicates before the task is to be executed.

// gradle hello -PskipHello task hello { doLast { println 'hello world' } } hello.onlyIf { ! project.hasProperty('skipHello') }Copy the code

Using StopExecutionException

If the logic of skipping a task cannot be represented by a predicate, StopExecutionException can be used. If an action raises this exception, further execution of the action and any subsequent actions of the task are skipped. The build moves on to the next task.

// gradle -q myTask task compile { doLast { println 'We are doing the compile.' } } compile.doFirst { // Here you would put arbitrary conditions in real life. // But this is used in an integration test so we want defined behavior. if (true)  { throw new StopExecutionException() } } task myTask { dependsOn('compile') doLast { println 'I am not affected' } }Copy the code

Enable and disable tasks

Each task has a enabled flag which defaults to true. Setting it to false prevents any task action from being performed. Disabled tasks are marked skip.

task disableMe {
    doLast {
        println 'This should not be printed if the task is disabled.'
    }
}
disableMe.enabled = false
Copy the code

Mission timeouts

Each task has a timeout property that can be used to limit its execution time. When a task reaches timeout, the task execution thread is interrupted. The task will be marked as failed. The finalizer task will still run. If –continue is used, other tasks can continue after this point. Tasks that do not respond to interrupts cannot time out. All of Gradle’s built-in tasks respond to timeout in a timely manner

task hangingTask() {
    doLast {
        Thread.sleep(100000)
    }
    timeout = Duration.ofMillis(500)
}
Copy the code

Task rules

Sometimes you want to perform a task whose behavior depends on a large or infinite number of parameter values. A good way to provide such tasks is with task rules:

// gradle -q pingServer1
tasks.addRule("Pattern: ping<ID>") { String taskName ->
    if (taskName.startsWith("ping")) {
        task(taskName) {
            doLast {
                println "Pinging: " + (taskName - 'ping')
            }
        }
    }
}
Copy the code

Rules are not only used when calling tasks from the command line. You can also create dependsOn relationships on rules-based tasks:

// gradle -q groupPing
tasks.addRule("Pattern: ping<ID>") { String taskName ->
    if (taskName.startsWith("ping")) {
        task(taskName) {
            doLast {
                println "Pinging: " + (taskName - 'ping')
            }
        }
    }
}

task groupPing {
    dependsOn pingServer1, pingServer2
}
Copy the code

If you run “Gradle -q Tasks”, you will not find the tasks named “pingServer1” or “pingServer2”, but the script is executing the logic based on the request to run these tasks.

Finalizer tasks

Finalizer Tasks are automatically added to the task diagram when a Finalizer task is scheduled to run.

// gradle -q taskX task taskX { doLast { println 'taskX' } } task taskY { doLast { println 'taskY' } } taskX.finalizedBy  taskYCopy the code

Finalizer Tasks are executed even if the termination task fails.

// gradle -q taskX
task taskX {
    doLast {
        println 'taskX'
        throw new RuntimeException()
    }
}
task taskY {
    doLast {
        println 'taskY'
    }
}

taskX.finalizedBy taskY 
Copy the code

Running results:

Output of gradle -q taskX
> gradle -q taskX
taskX
taskY

FAILURE: Build failed with an exception.

* Where:
Build file '/home/user/gradle/samples/groovy/build.gradle' line: 4

* What went wrong:
Execution failed for task ':taskX'.
> java.lang.RuntimeException (no error message)

* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output. Run with --scan to get full insights.

* Get more help at https://help.gradle.org

BUILD FAILED in 0s
Copy the code

Finalizer tasks are useful in situations where you build resources that must be cleaned up whether the build fails or succeeds. An example of such a resource is a Web container that starts before integration test tasks and should always be closed even if some tests fail.

To specify a finalizer Task, use task.finalizedby (java.lang.object…). Methods. This method accepts the Task instance, Task name or task.dependson (java.lang.object…) Any other input accepted.

That’s the end of the task explanation.