Gradle 构建学习(二)---------详解 task

726 阅读4分钟

gradle 执行过程

1.初始化阶段 2.配置阶段 3.执行阶段

初始化阶段 其实对应的就是我们的settings.gradle 文件。 配置阶段 就是主要目的就是为了构建出我们的 task和task之间的依赖关系 也就是谁先执行 谁后执行的 有向无环图。 执行阶段 就是整个gradle 最终 build 你project的 阶段了

task 定义以及配置

task的定义其实比较重要的就是group属性了,很多人都忽略了这个属性,导致定义的task 全都在默认的other分组中,找起来很难找。

也可以看看 task 总共有哪些属性可以设置

这里要注意了,task里面代码的执行时机,比如说

//默认是在other分组 大部分的blog就只写了这种方式
task helloTask {
    println 'helloTask1 '
    doFirst {
        println("helloTask2")
    }
}

看下执行结果

也就是说你在task里面 直接写的语句 他是在 config阶段去执行的。 只有写在dofirst 或者dolast 必包里面的 才是最终在gradle执行阶段执行的

统计assembleDefaultDebug 时长

有了上述的基础,我们来做个简单的小功能,统计一下 build 这个task 的耗时。 要统计他的耗时 我们肯定要知道这个assembleDefaultDebug task是从哪里开始的。这个我们只要输入一下这个build的命令 然后查看一下 从哪个task开始的即可

从上图可得知 这是从preBuild 这个task开始的。

this.afterEvaluate { Project project ->
    def preTask = project.tasks.getByName('preBuild')
    def t1
    preTask.doFirst {
        t1 = System.currentTimeMillis()
    }
    def buildTask = project.tasks.getByName('assembleDefaultDebug')
    buildTask.doLast {
        println("build takes:" + (System.currentTimeMillis() - t1))
    }
}

看下执行结果

task 依赖

网上一些随处可见的 依赖相关知识 我就不多重复了,我讲点 别人没提到的。

首先 一个task 是可以依赖 多个task的。 并不是说一个task 只能depends 一个task

task taskX{
    doLast {
        println "taskX"
    }
}
task taskY{
    doLast {
        println "taskY"
    }
}

task taskZ(dependsOn:[taskX,"assembleDebug"]){
    doLast {
        println "taskZ"
    }
}

此外,上述的例子中 taskZ依赖于 task X和task Y,千万不要以为我把task X 写在taskY的前面 就代表 x先于y的执行。 网上有很多人说 这里taskX和taskY的执行顺序是 随机的,但是从我自己多次测试的结果来看,似乎并不是这样,而是根据taskX和taskY的 名称来的,从第一个字母开始 字母越大的执行的时候 就在前面。 可能因为我gradle版本过高的缘故?反正这里大家要小心一点,可以多做做测试,留言板上与我交流

另外task还可以动态依赖。 比如说你可以依赖某些 符合某些规则的task。

task taskZ{
    // 通过程序的输出来指定依赖
    dependsOn this.tasks.findAll { task ->
        return task.name.startsWith("build")
    }

    doLast {
        println "taskZ"
    }
}

task 输入输出

我们首先看一下 程序的输出

可以看出来 只支持 文件或者文件路径的输出。

再看下输入,这里也很明显了, 除了支持文件和文件路径的输入以外 还支持map类型的输入,注意这个map的value 是object 也就是说 可以支持任何类型。

做一个小功能,我们把我们android minSdk和versionSdk 等等 输出到一个文件里。

ext {
    mMinSdkVersion = 26
    mTargetSdkVersion = 29
    infoFile = file(this.projectDir.toString() + "/info.txt")
    if (!infoFile.exists()) {
        infoFile.createNewFile()
    }
}

先定义 version然后定义 输出的文件。

看下写文件任务

task writeInfoTask() {
    inputs.property("mMinSdkVersion", this.mMinSdkVersion.toString())
    inputs.property("mTargetSdkVersion", this.mTargetSdkVersion.toString())
    outputs.files this.infoFile
    doLast {
        println("writeInfoTask")
        def data = inputs.getProperties()
        File file = outputs.getFiles().getSingleFile()
        file.write(data.toString())
    }
}
task zreadInfoTask() {
    inputs.files this.infoFile
    doLast {
        println("readInfoTask")
        File file = inputs.getFiles().getSingleFile()
        println("file content:" + file.text)
    }
}

最后我们来执行一下这个任务

task taskX {
    dependsOn(writeInfoTask, zreadInfoTask)
    doLast {
        println("task all over")
    }
}

注意看我这里为啥不叫readInfoTask而是叫zredInfoTask? 因为网上有很多人说 gradle可以智能判定,当一个task的输入依赖一个文件,而这个文件又是另外一个task的输出的时候,gradle可以智能的将 输出文件的task放在前面, 输入文件的task放在后面。 但是我这里测试的结果来看,并不是这样。 大家可以自行试试,看看是不是和gradle的版本有关 导致这个特性不同。

此外还有一个mustRunAfter函数 也可以保证在某个任务执行完毕以后执行。有兴趣的可以自行探索一番。