Almost everything in Gradle is based on these two fundamental concepts:
- task
- project
Master these two and you've mastered more than half of your Gradle knowledge.
Let's talk about Task first
Literally understood as a task, all events executed in Gradle are executed by Task.
For example, we create a new Android project and enter in its root directory:
gradle tasks -q
You can see the following output (you may need to configure gradle environment variables beforehand, or you can use ./gradlew
alternatives):
According to the above figure, you can see that each item in the current project has task
been listed, and there is a yellow output indicating the current task
description.
Among them, -q
it means to ignore gradle
its own log
information. Adding this parameter can block many irrelevant outputs, and it will not affect the execution if it is not added.
Task declaration format
To declare a task, you only need to add task
it in front of the task name, for example, a hello
Task is declared below.
task hello
Usually we attach some execution actions to the task, called Action
, for example
hello.doFirst{
println "hello first"
}
hello.doLast{
println "hello last"
}
It can also be accompanied by a closure configuration, which is called Configuration
, the closure can not only be used for assignment operations, but also can perform some automatic configuration.
hello {
println "hello"
}
Task dependency
Declaring one alone task
will hardly make any sense in actual development. More often, multiple task
groups are combined, and one depends on the other to form a series of task sets.
task hello
hello.doFirst{
println "hello "
}
task world(dependsOn: "hello") << {
println "world"
}
The above code defines two tasks. When we execute the hello
task, it will output hello
, and when the world
task is executed, because it is declared dependsOn: "hello"
, it means world
dependency hello
, and hello will be executed first, and then the world will be executed.
task xxx << {
}
Such syntax is equivalent to
task xxx
xxx.dolast {
}
build.gradle
You can create a new text at any location called , to practice the task
definitions and dependencies described above.
Then talk about Project
Android
│
├──app
│ └──build.gradle
│
├──library
│ └──build.gradle
│
├──*.properties
│
├──build.gradle
│
└──setting.gradle
An Android project is usually composed of the above structure, which has many unknown and clever uses.
setting.gradle file
setting.gradle
Many people don't know that you can also write code in Chinese . The following code is an example I mentioned in the previous article [ Enterprise Android Modular Platform Design Recommendationssetting.gradle
]. In the file, you can specify a project
location, where you can import a module from an external project into the APP project .
getLocalProperties().entrySet().each { entry ->
def moduleName = entry.key
if (Boolean.valueOf(entry.value)) {
def file = new File(rootProject.projectDir.parent, "/${moduleName.replace("\\W", "")}/${moduleName.toLowerCase()}")
if (file.exists()) {
include ":${moduleName.toLowerCase()}"
project(":${moduleName.toLowerCase()}").projectDir = file
}
}
}
build.gradle
The root gradle file of a project is used to describe the unified resources of the project, including the usage of each sub-resource, the dependency environment of the plugin, and so on.
subprojects{
apply plugin: 'com.android.library'
dependencies {
compile 'com.xxx.xxx:xxx:1.0.0'
}
}
Usually, when we refer to aars in each module, we will manually go through it in each module compile
, such as support
packages. But there is actually a very simple way to write it once, that is to declare this in the closure in the root gradle
file of the project .subprojects
dependencies
Usually when writing compile
dependencies, we will write like this:
compile 'com.android.support:appcompat-v7:25.0.0'
In fact gradle
, this is a method call, and its essence is that the compile()
method passes in a map
parameter, so the complete writing is actually like this:
compile group: 'com.android.support' name:'appcompat-v7' version:'25.0.0'
At the same time, the usable keys of the map are not only the commonly used keys group
, name
, version
, but also the less commonly used keys configuration
, classifier
and so on.
Look again at Task
Groovy is based on Java, but it adds a lot of closures on this basis to help develop build scripts more conveniently. If you don't know Groovy, it doesn't matter, just write it in Java, in fact, it is most appropriate to write it in Kotlin. If you don't know Kotlin yet, I highly recommend you to check out my [ Kotlin Primer ] series of articles
Each Task can configure its input and output. If the output of a Task is consistent with the previous output, it will not be executed repeatedly. UP-TO-DATE
At this point, it will output the latest results on the command line .
For example the following Task:
task transform {
ext.srcFile = file('hello.txt')
ext.destDir = new File(buildDir, 'generated')
inputs.file srcFile
outputs.dir destDir
doLast {
destDir.mkdirs()
def ins = new BufferedReader(new FileReader(srcFile))
def stringBuilder = new StringBuilder()
def temp
while ((temp = ins.readLine()) != null) {
stringBuilder.append(temp)
}
def destFile = new File(destDir, "world.txt")
destFile.text = stringBuilder.toString()
}
}
It will output after repeated executionUP-TO-DATE
Behind the action
The fastest way to learn any technology is to look at the source code. gradle
The source code is located in the src
directory. For example, the path on my computer is:
/Users/zhangtao/.gradle/wrapper/dists/gradle-3.3-all/55gk2rcmfc6p2dg9u9ohc3hw9/gradle-3.3/src
create a new ordinary Java project locally, import the source code to view the code and comments, this is the best learning material .
task hello
In Groovy, method parentheses can be omitted, and quotes can also be omitted if the type of the string can be inferred
public interface org.gradle.api.Project{
Task task(String name);
Task task(String name, Closure configureClosure);
}
// TaskFactory
public TaskInternal createTask(Map<String, ?> args) {
}
Closures exist to better initialize objects. As in Kotlin, parentheses can be omitted when a closure is used as the last parameter.
Copy a = task(myCopy, type: Copy)
a.from 'resources'
a.into 'target'
a.include('**/*.txt', '**/*.xml', '**/*.properties')
Equivalent to
task myCopy(type: Copy)
myCopy {
from 'resources'
into 'target'
include('**/*.txt', '**/*.xml', '**/*.properties')
}
That's it for this chapter, and the next one is about how to create a Gradle plugin to dynamically add code (including jar packages) to a specified class or a newly generated class at compile time.