构建核心,Gradle Task 10 个知识点
本文作者
作者:yechaoa
链接:
https://juejin.cn/post/7248207744087277605
本文由作者授权发布。
本文是Gradle系列的第7篇,给大家带来Gradle构建核心Task相关的知识点。
https://juejin.cn/column/7123935861976072199
Task管理了一个Action的List,你可以在List前面插入一个Action(doFirst),也可以从list后面插入(doLast),Action是最小的执行单元。
3.1、Task写在哪
我们在Gradle系列的第4篇生命周期中介绍到,有三个阶段,第一个阶段初始化会决定哪些项目参与编译,第二个阶段就是解析配置,会生成Task注册表(DAG),第三个阶段就是依次执行Task。
https://juejin.cn/post/7170684769083555877
3.2、创建Task
register(String name, Action<? super Task> configurationAction) register(String name, Class type, Action<? super T> configurationAction) register(String name, Class type) register(String name, Class type, Object... constructorArgs) register(String name)
configurationAction指的是Action,也就是该Task的操作,会在编译时执行。 type类型指的是Task类型,可以是自定义类型,也可以指定自带的Copy、Delete、Zip、Jar等类型。
tasks.register("yechaoa") {
println("Task Name = " + it.name)
}
上面调用的就是register的方式1,最后一个参数如果是闭包,可以写在参数外面。
task("yechaoa") {
println "aaa"
}
通过Plugin的方式也可以创建Task,重写的apply方法会有Project对象。
4.1、执行单个Task
./gradlew taskname
tasks.register("yechaoa") {
println("Task Name = " + it.name)
}
./gradlew yechaoa
Task Name = yechaoa
4.2、执行多个Task
./gradlew taskname taskname taskname
4.3、Task同名
* What went wrong:
A problem occurred evaluating project ':app'.
> Cannot add task 'yechaoa' as a task with that name already exists.
4.4、Task执行结果
> Task :app:createDebugVariantModel UP-TO-DATE
> Task :app:preBuild UP-TO-DATE
> Task :app:preDebugBuild UP-TO-DATE
> Task :app:mergeDebugNativeDebugMetadata NO-SOURCE
> Task :app:compileDebugAidl NO-SOURCE
> Task :app:compileDebugRenderscript NO-SOURCE
> Task :app:generateDebugBuildConfig UP-TO-DATE
.....
4.4.1、EXCUTED
4.4.2、UP-TO-DATE
> Task :app:preBuild UP-TO-DATE
输入和输出都没有改变; 输出没有改变; Task没有操作,有依赖,但依赖的内容是最新的,或者跳过了,或者复用了; Task没有操作,也没有依赖。
4.4.3、FOME-CACHE
4.4.4、SKIPPED
$ gradle dist --exclude-task yechaoa
4.4.5、NO-SOURCE
Task不需要执行。有输入和输出,但没有来源。
5.1、Action
5.1.1、自定义Task
class YechaoaTask extends DefaultTask {
@Internal
def taskName = "default"
@TaskAction
def MyAction1() {
println("$taskName -- MyAction1")
}
@TaskAction
def MyAction2() {
println("$taskName -- MyAction2")
}
}
自定义一个类,继承自DefaultTask; Action的方法需要添加@TaskAction注解; 对外暴露的参数需要使用@Internal注解;
tasks.register("yechaoa", YechaoaTask) {
taskName = "我是传入的Task Name "
}
> Task :app:yechaoa
我是传入的Task Name -- MyAction1
我是传入的Task Name -- MyAction2
tasks.register('yechaoa', YechaoaTask, 'xxx')
5.2、doFirst
5.3、doLast
tasks.register("yechaoa") {
it.doFirst {
println("${it.name} = doFirst 111")
}
it.doFirst {
println("${it.name} = doFirst 222")
}
println("Task Name = " + it.name)
it.doLast {
println("${it.name} = doLast 111")
}
it.doLast {
println("${it.name} = doLast 222")
}
}
Task Name = yechaoa
> Task :app:yechaoa
yechaoa = doFirst 222
yechaoa = doFirst 111
yechaoa = doLast 111
yechaoa = doLast 222
5.4、Action执行顺序
doFirst:倒序 Action:正序 doLast:正序
String TASK_NAME = "name";
String TASK_DESCRIPTION = "description";
String TASK_GROUP = "group";
String TASK_TYPE = "type";
String TASK_DEPENDS_ON = "dependsOn";
String TASK_OVERWRITE = "overwrite";
String TASK_ACTION = "action";
7.1、dependsOn
tasks.register("yechaoa111") {
it.configure {
dependsOn(provider {
tasks.findAll {
it.name.contains("yechaoa222")
}
})
}
it.doLast {
println("${it.name}")
}
}
tasks.register("yechaoa222") {
it.doLast {
println("${it.name}")
}
}
./gradlew yechaoa111
> Task :app:yechaoa222
yechaoa222
> Task :app:yechaoa111
yechaoa111
def yechaoa111 = tasks.register("yechaoa111") {
it.doLast {
println("${it.name}")
}
}
def yechaoa222 = tasks.register("yechaoa222") {
it.doLast {
println("${it.name}")
}
}
yechaoa111.configure {
dependsOn yechaoa222
}
dependsOn tasks.withType(Copy)
dependsOn "project-lib:yechaoa"
7.2、finalizedBy
task taskY {
finalizedBy "taskX"
}
7.3、mustRunAfter
def yechaoa111 = tasks.register("yechaoa111") {
it.doLast {
println("${it.name}")
}
}
def yechaoa222 = tasks.register("yechaoa222") {
it.doLast {
println("${it.name}")
}
}
yechaoa111.configure {
mustRunAfter yechaoa222
}
./gradlew yechaoa111
> Task :app:yechaoa111
yechaoa111
./gradlew yechaoa111 yechaoa222
> Task :app:yechaoa222
yechaoa222
> Task :app:yechaoa111
yechaoa111
7.4、shouldRunAfter
yechaoa111.configure {
shouldRunAfter yechaoa222
}
shouldRunAfter规则类似,但不太一样,因为它在两种情况下会被忽略。首先,如果使用该规则会引入一个排序周期;其次,当使用并行执行时,除了“应该运行”任务外,任务的所有依赖项都已满足,那么无论其“应该运行”依赖项是否已运行,都将运行此任务。
条件跳过 异常跳过 禁用跳过 超时跳过
8.1、条件跳过
tasks.register("skipTask") { taskObj ->
taskObj.configure {
onlyIf {
def provider = providers.gradleProperty("yechaoa")
provider.present
}
}
taskObj.doLast {
println("${it.name} is Executed")
}
}
./gradlew skipTask -Pyechaoa
> Task :app:skipTask
skipTask is Executed
8.2、异常跳过
tasks.register("skipTask") { taskObj ->
taskObj.doFirst {
println("${it.name} is Executed doFirst")
}
taskObj.doLast {
def provider = providers.gradleProperty("yechaoa")
if (provider.present) {
throw new StopExecutionException()
}
println("${it.name} is Executed doLast")
}
}
> Task :app:skipTask
skipTask is Executed doFirst
8.3、禁用跳过
tasks.register("skipTask") { taskObj ->
taskObj.configure {
enabled = true
}
taskObj.doLast {
println("${it.name} is Executed")
}
}
8.4、超时跳过
tasks.register("skipTask") { taskObj ->
taskObj.configure {
timeout = Duration.ofSeconds(10)
}
taskObj.doLast {
Thread.sleep(11 * 1000)
println("${it.name} is Executed")
}
}
> Task :app:skipTask FAILED
Requesting stop of task ':app:skipTask' as it has exceeded its configured timeout of 10s.
---Gradle:buildFinished 构建结束了
FAILURE: Build failed with an exception.
* What went wrong:
Execution failed for task ':app:skipTask'.
> Timeout has been exceeded
执行异常了,并提示timeout of 10s,因为我在doLast中sleep了11s。
9.1、输入和输出
上面的两段理论摘自官方,对于新手来说,可能有点晦涩难懂,下面会带大家实操一下。
9.2、增量构建的两种形式
第一种,Task完全可以复用,输入和输出都没有任何变化,即UP-TO-DATE。 第二种,有部分变化,只需要针对变化的部分进行操作。
9.3、案例实操
class CopyTask extends DefaultTask {
// 指定输入
@InputFiles
FileCollection from
// 指定输出
@OutputDirectory
Directory to
// task action 执行
@TaskAction
def execute() {
File file = from.getSingleFile()
if (file.isDirectory()) {
from.getAsFileTree().each {
copyFileToDir(it, to)
}
} else {
copyFileToDir(from, to)
}
}
/**
* 复制文件到文件夹
* @param src 要复制的文件
* @param dir 接收的文件夹
* @return
*/
private static def copyFileToDir(File src, Directory dir) {
File dest = new File("${dir.getAsFile().path}/${src.name}")
if (!dest.exists()) {
dest.createNewFile()
}
dest.withOutputStream {
it.write(new FileInputStream(src).getBytes())
}
}
}
上面代码中from就是我们的输入,即要复制的文件; to是我们的输出,即要接收的文件夹; 然后execute()方法就是Task执行的Action。
tasks.register("CopyTask", CopyTask) {
from = files("from")
to = layout.projectDirectory.dir("to")
}
├── app
│ ├── from
│ │ └── txt1.txt
./gradlew CopyTask
├── app
│ ├── from
│ │ └── txt1.txt
│ └── to
│ └── txt1.txt
9.3.1、UP-TO-DATE
➜ GradleX git:(master) ✗ ./gradlew CopyTask
...
BUILD SUCCESSFUL in 1s
1 actionable task: 1 executed
➜ GradleX git:(master) ✗ ./gradlew CopyTask
...
BUILD SUCCESSFUL in 1s
1 actionable task: 1 up-to-date
9.3.2、增量构建
class CopyTask extends DefaultTask {
// 指定增量输入属性
@Incremental
// 指定输入
@InputFiles
FileCollection from
// 指定输出
@OutputDirectory
Directory to
// task action 执行
@TaskAction
void execute(InputChanges inputChanges) {
boolean incremental = inputChanges.incremental
println("isIncremental = $incremental")
inputChanges.getFileChanges(from).each {
if (it.fileType != FileType.DIRECTORY) {
ChangeType changeType = it.changeType
String fileName = it.file.name
println("ChangeType = $changeType , ChangeFile = $fileName")
if (changeType != ChangeType.REMOVED) {
copyFileToDir(it.file, to)
}
}
}
}
/**
* 复制文件到文件夹
* @param src 要复制的文件
* @param dir 接收的文件夹
* @return
*/
static def copyFileToDir(File file, Directory dir) {
File dest = new File("${dir.getAsFile().path}/${file.name}")
if (!dest.exists()) {
dest.createNewFile()
}
dest.withOutputStream {
it.write(new FileInputStream(file).getBytes())
}
}
}
给from属性增加@Incremental注解,表示增量输入属性; 重写了action方法execute(),增加了InputChanges参数,支持增量复制文件,然后根据文件的ChangeType做校验,只复制新增或修改的文件。
public enum ChangeType {
ADDED,
MODIFIED,
REMOVED
}
ADDED:表示文件是新增的; MODIFIED:表示文件是修改的; REMOVED:表示文件被删除;
./gradlew CopyTask
> Task :app:CopyTask
isIncremental = false
ChangeType = ADDED , ChangeFile = txt1.txt
BUILD SUCCESSFUL in 2s
1 actionable task: 1 up-to-date
9.3.3、ADDED
> Task :app:CopyTask
isIncremental = true
ChangeType = ADDED , ChangeFile = txt2.txt
├── app
│ ├── build.gradle
│ ├── from
│ │ └── txt1.txt
│ └── txt2.txt
│ └── to
│ ├── txt1.txt
│ └── txt2.txt
9.3.4、MODIFIED
> Task :app:CopyTask
isIncremental = true
ChangeType = MODIFIED , ChangeFile = txt1.txt
9.3.5、REMOVED
> Task :app:CopyTask
isIncremental = true
ChangeType = REMOVED , ChangeFile = txt2.txt
├── app
│ ├── from
│ │ └── txt1.txt
│ └── to
│ ├── txt1.txt
│ └── txt2.txt
9.4、增量vs全量
该Task是第一次执行; 该Task只有输入没有输出; 该Task的upToDateWhen条件返回了false; 自上次构建以来,该Task的某个输出文件已更改; 自上次构建以来,该Task的某个属性输入发生了变化,例如一些基本类型的属性; 自上次构建以来,该Task的某个非增量文件输入发生了变化,非增量文件输入是指没有使用@Incremental或@SkipWhenEmpty注解的文件输入。
9.5、常用的注解类型
注解 | 类型 | 含义 |
更多可查看文档。
https://docs.gradle.org/current/userguide/incremental_build.html
9.6、增量构建原理
Task的输入还用于计算启用时用于加载Task输出的构建缓存密钥。
findByPath(String path),参数可空 getByPath(String path),参数可空,找不到Task会抛异常UnknownTaskException
findByName getByName
10.1、findByName
def aaa = tasks.findByName("yechaoa").doFirst {
println("yechaoa excuted doFirst by findByName")
}
./gradlew yechaoa
> Task :app:yechaoa
yechaoa excuted doFirst by findByName
...
10.2、findByPath
def bbb = tasks.findByPath("yechaoa").doFirst {
println("yechaoa excuted doFirst by findByPath")
}
> Task :app:yechaoa
yechaoa excuted doFirst by findByPath
yechaoa excuted doFirst by findByName
...
10.3、named
tasks.named("yechaoa") {
it.doFirst {
println("yechaoa excuted doFirst by named")
}
}
> Task :app:yechaoa
yechaoa excuted doFirst by named
yechaoa excuted doFirst by findByPath
yechaoa excuted doFirst by findByName
...
10.4、其他
tasks.withType(DefaultTask).configureEach(task -> {
if (task.name.toLowerCase().contains("copytask")) {
println(task.class.name)
}
})
tasks.each {
// do something
}
tasks.forEach(task->{
// do something
})
tasks.configureEach(task -> {
// do something
})
11.1、register和create的区别
通过register创建时,只有在这个task被需要时,才会创建和配置; 通过create创建时,则会立即创建与配置该Task,并添加到TaskContainer中。
除了register和create,还有一个replace方法,用于替换名称已存在的Task。
11.2、Task Tree
要查看Task的依赖关系,我们可以使用task-tree插件。
plugins {
id "com.dorongold.task-tree" version "2.1.1"
}
gradle <task 1>...<task N> taskTree
./gradlew build taskTree
:app:build
+--- :app:assemble
| +--- :app:assembleDebug
| | +--- :app:mergeDebugNativeDebugMetadata
| | | --- :app:preDebugBuild
| | | --- :app:preBuild
| | --- :app:packageDebug
| | +--- :app:compileDebugJavaWithJavac
| | | +--- :app:compileDebugAidl
| | | | --- :app:preDebugBuild *
| | | +--- :app:compileDebugKotlin
......
Github
最后推荐一下我做的网站,玩Android: wanandroid.com ,包含详尽的知识体系、好用的工具,还有本公众号文章合集,欢迎体验和收藏!
推荐阅读:
扫一扫 关注我的公众号
如果你想要跟大家分享你的文章,欢迎投稿~
┏(^0^)┛明天见!