本文已参与「新人创作礼」活动,一起开启掘金创作之路
什么是Task
gradle构建框架的核心是Task,何为Task,其实就是具体的构建任务,例如打包生成一个Apk就是一个Task,在AndroidStudio中,右侧的gradle工具点开可以看到当前工程的gradle结构视图,里面就能看到每个module下所有的Task,例如app模块下的assemble就是一个Task,这个Task可以完整编译打包一个apk
Task其实就类似于某个功能类,例如我们平常写一个简单的网络请求模块,我们至少会分解成几个主要的子类去做吧,包括请求参数的构造类,具体请求的发起执行类,请求结果的回调处理类,这样做的好处就是,解耦和提高复用性
那么类比到构建中也一样,我们打包Apk有很多的子任务要去做,包括编译资源,处理aidl,编译class等等,所以肯定不能在一个Task中去完成所有的事情,这样是没法迭代维护的,自然就需要分解成多个类模块
Task也不是gradle中执行逻辑的最小单元,真正的最小单元是Task中的Action,一个Task也可以有多个Action,Action就可以类比到一个类中分解的多个函数
自定义Task
自定义Task其实还比较简单,继承DefaultTask实现一个自定义Task类,然后在类中通过注解@TaskAction修饰方法,这个就是去定义了一个具体的Action,如果定义了多个Action,执行顺序是顺序的,也就是定义在上面的会先执行
class MyTask extends DefaultTask {
@TaskAction
def action1(){
println "myTask action1"
}
@TaskAction
def action2(){
println "myTask action2"
}
}
现在已经自定义了一个Task,但并没有入口可以去执行它,所以这里还需要使用到Project类(gradle中的关键类,例如build.gradle脚本都会被编译成一个Project对象gradle系列第一篇有介绍到)中的task方法,去创建一个对应的task,说白了就是在build.gradle脚本中去用task接口声明创建一个自定义Task类型的Task
task doMyTask(type: MyTask){
}
创建task之后,sync一下,就会在前面所说的gradle视图中,相应模块:Tasks:other路径下看到创建的这个task,此时这个task就是可执行的
执行结果:
> Task :library1:doMyTask
myTask action1
myTask action2
buildFinished
Action不仅可以在自定义Task类的时候去定义,也可以在创建具体task的时候去添加,主要有两种,doFirst和doLast,doFirst就是添加一个action到该Task的action集合的最前面,doLast就是添加一个action到当前action集合的最后,doFirst和doLast都可以添加多个
这个特性的作用就是针对非自定义的Task,可以在这个Task执行的时候,去做一些自定义的操作,
task doMyTask(type: MyTask){
// 可以在内部直接添加
doFirst{
println "myTask doFirst"
}
doLast {
println "myTask doLast"
}
}
// 也可以在外部添加
doMyTask.doFirst {
println "myTask doFirst1"
}
doMyTask.doLast {
println "myTask doLast1"
}
执行结果
> Task :library1:doMyTask
myTask doFirst1
myTask doFirst
myTask action1
myTask action2
myTask doLast
myTask doLast1
buildFinished
创建task时,除了使用doFirst和doLast闭包中编写逻辑添加新的Action外,当然也可以在task内直接写逻辑,不过这部分代码就不属于任何Action了,也就是这部分代码不是在task的执行阶段去执行的,而是属于配置阶段的代码(这个涉及到gradle的执行生命周期),这里先大概了解下,后面也会详细介绍gradle的生命周期
task doMyTask(type: MyTask){
println "myTask config"
doFirst{
println "myTask doFirst"
}
...
}
// 执行结果
// 配置阶段
> Configure project :library1
myTask config
// 执行阶段
> Task :library1:doMyTask
myTask doFirst1
myTask doFirst
myTask action1
myTask action2
myTask doLast
myTask doLast1
buildFinished
Task依赖
前面提到一个具体的构建任务是由多个Task串起来执行,那具体就需要有一个方法来控制不同Task的执行先后顺序,这就是依靠依赖实现的
例如打包Apk,表面上是只执行了assemble一个Task,但实际这个Task依赖了很多其他的Task,例如间接依赖把java编译成class的Task,这个Task又依赖aidl编译成java的task,这些Task也都是各自存在上层依赖,这样从最上层的Task开始一层层执行下来,最后打包成一个apk
依赖可以用dependsOn来实现,如A.dependsOn B,表示A的执行依赖于B,当执行Task A时,会先执行TaskB
task B {
doFirst {
println "task B"
}
}
// 定义task的时候,可以直接指定以来的task
task A(dependsOn: B) {
doFirst {
println "task A"
}
}
// 也可以在外部调用dependsOn方法指定依赖
A.dependsOn B
执行Task A,终端显示在执行A之前先执行了B
> Task :library1:B
task B
> Task :library1:A
task A
buildFinished
当然一个Task可以同时依赖多个Task
task B {
doFirst {
println "task B"
}
}
task C {
doFirst {
println "task C"
}
}
task A(dependsOn: [B,C]) {
doFirst {
println "task A"
}
}
// 或者
A.dependsOn B,C
// 执行结果
> Task :library1:B
task B
> Task :library1:C
task AA
> Task :library1:A
task A
buildFinished
gradle构建生命周期
生命周期其实就是指使用gradle命令执行一个task时,gradle背后是怎么去流转执行的,了解了这个就能知道那些setting.gradle,build.gradle等脚本里面的代码是怎么去执行的,脚本之间是怎么联系起来的等等
构建流程整体上是分成三个大阶段
- 初始化阶段
这个阶段主要执行init.gradle 和 setting.gradle,产出的结果就是会生成几个关键的对象,包括gradle对象,setting对象以及project对象。
- 执行init.gradle会生成一个全局的gradle对象,这个在所有脚本中都是共用的,通过这个对象可以去设置一个监听hook点等;
- 执行setting.gradle脚本会生成setting对象以及主project对象和每个子module对应的project对象
- 配置阶段 配置阶段主要执行所有build.gradle脚本里面的内容,从根项目下的build.gradle开始执行,产出的结果是生成Task的有向无环图,即Task集合执行流程图
正常build.gradle的执行顺序是从上往下按顺序执行,但是在根项目下的build.gradle中,buildscript闭包不管写在什么位置,一定是第一个执行的
如果你在这些build.gradle中,创建了一些task,就会去执行task中的配置代码,也就是上面提到的除了doFirst和doLast之外的代码
- 执行阶段 执行阶段即按照配置阶段生成的task有向无环图,一个一个的执行具体Task中的Action
tips
在AndroidStudio中,点击sync其实也是去执行一个gradle命令,也会去走这些流程,只是没有具体的task执行,因为在配置阶段生成的task有向无环图是个空的,没有任何task需要执行,可以在项目中打印看下
// 这个就是生命周期的hook,下面马上会讲到
gradle.getTaskGraph().addTaskExecutionGraphListener(new TaskExecutionGraphListener() {
@Override
void graphPopulated(TaskExecutionGraph taskExecutionGraph) {
println "taskGraph:${taskExecutionGraph.allTasks}"
}
})
gradle生命周期Hook
什么是Hook
之所以写这个是因为我最开始学习的时候,对于这种比较高深的词汇就有种畏惧,感觉会很复杂,但其实每个高深的词汇后面都是很熟悉的一些逻辑
Hook就是在别人某段代码逻辑里面上添加我们的逻辑,类比Android开发,我们最开始接触的就是生命周期,如Activity的OnCreate,onResume等,我们重写这些函数,其实就是去Hook了Activity的生命周期,所以Hook实际就是在某段代码逻辑的执行过程中加了一些接口的回调,外部使用者可通过实现接口,在已有的流程当中注入一些自己的代码
gradle里面有哪些Hook点
首先说明下要Hook住gradle的执行流程,需要在setting.gradle文件中去编写,因为gradle提供的Hook点主要就是在setting.gradle脚本执行完成之后,如果在某个module的build.gradle中去写hook逻辑,可能执行到注册hook的代码的时候,时机已经过了,而在setting.gradle执行之前,只有init.gradle,这个脚本是全局的,在这里面hook的话可能会影响其他项目,而且一般也不会这样去操作这个文件
gradle的Hook函数,说实话我刚学的时候感觉太乱了,函数多不说,函数名称上也不好区分具体是什么时机调用的,所以如果第一遍没看懂的话,最好多看几遍
Hook函数我从用法上对它进行了区分,主要分成两大种,一种是单个方法的注册监听,使用了闭包;另外一种则是通过添加Listener,可以一次复写多个Hook接口,一下是常用的一些Hook接口,代码中有详细注释
单个Hook点监听
// setting脚本执行之前,这个写在setting脚本中没意义,因为已经开始执行了,通常也用不上
gradle.beforeSettings {
println "beforeSettings"
}
// setting 脚本执行完成之后调用
gradle.settingsEvaluated {
println "settingsEvaluated"
}
// 所有的project对象加载完成,其实就是在setting.gradle脚本执行完成之后,
// 在settingsEvaluated回调之后,初始化阶段完成
gradle.projectsLoaded {
println "projectsLoaded"
}
// 每个module的build.gradle执行之前都会调用,闭包会传入当前的project对象作为参数
// 闭包需要看官方文档的说明
// Adds a closure to be called immediately before a project is evaluated. The project is passed to the closure as a parameter.
gradle.beforeProject { project ->
println "beforeProject $project"
}
// 每个module的build.gradle脚本执行完成后调用,参数同beforeProject
gradle.afterProject { project ->
println "afterProject $project"
}
// 所有module的build.gradle脚本执行完成之后
gradle.projectsEvaluated {
println "projectsEvaluated"
}
// task有向无环图构建完成,配置阶段完成,TaskExecutionGraph对象作为对象传入闭包
gradle.taskGraph.whenReady { graph ->
println "taskGraph.whenReady ${graph.getAllTasks()}"
}
// 构建任务完成
gradle.buildFinished {
println "buildFinished"
}
gradle.allprojects(new Action<Project>() {
@Override
void execute(Project project) {
// 回调时机也是在单个module的build.gradle脚本执行之前,在gradle.beforeProject之后,
// 跟gradle.beforeProject的hook效果基本一样
project.beforeEvaluate {
println "project.beforeEvaluate $it"
}
// 同上,跟gradle.afterProject对应
project.afterEvaluate {
println "project.afterEvaluate $it"
}
}
})
使用Listener
addBuildListener
gradle.addBuildListener(new BuildListener() {
// 构建开始,这个没啥意义,内部用的
@Override
void buildStarted(Gradle gradle) {
println "BuildListener.buildStarted"
}
// 同gradle.settingsEvaluated
@Override
void settingsEvaluated(Settings settings) {
println "BuildListener.settingsEvaluated"
}
// 同gradle.projectsLoaded
@Override
void projectsLoaded(Gradle gradle) {
println "BuildListener.projectsLoaded"
}
// 同gradle.projectsEvaluated
@Override
void projectsEvaluated(Gradle gradle) {
println "BuildListener.projectsEvaluated"
}
// 同gradle.buildFinished
@Override
void buildFinished(BuildResult buildResult) {
println "BuildListener.buildFinished"
}
})
addListener
这个比较灵活,因为接受的参数是一个Object,可以使用上述的多种Listener
TaskExecutionListener
gradle.addListener(new TaskExecutionListener() {
// 每个Task执行之前
@Override
void beforeExecute(Task task) {
println "TaskExecutionListener.beforeExecute"
}
// 每个Task执行之后
@Override
void afterExecute(Task task, TaskState taskState) {
println "TaskExecutionListener.afterExecute"
}
})
TaskActionListener
gradle.addListener(new TaskActionListener() {
// Task中所有Action执行之前
@Override
void beforeActions(Task task) {
println "TaskActionListener.beforeActions"
}
// Task中所有Action执行之后
@Override
void afterActions(Task task) {
println "TaskActionListener.afterActions"
}
})
DependencyResolutionListener
gradle.addListener(new DependencyResolutionListener() {
// 子module的build.gradle脚本执行完之后,进行依赖决策之前,在对应afterEvaluate之后
@Override
void beforeResolve(ResolvableDependencies resolvableDependencies) {
println "DependencyResolutionListener.beforeResolve"
}
// 进行依赖决策之后
@Override
void afterResolve(ResolvableDependencies resolvableDependencies) {
println "DependencyResolutionListener.afterResolve"
}
})
TaskExecutionGraphListener
gradle.addListener(new TaskExecutionGraphListener() {
// 同gradle.taskGraph.whenReady
@Override
void graphPopulated(TaskExecutionGraph taskExecutionGraph) {
println "TaskExecutionGraphListener.graphPopulated"
}
})
addTaskExecutionGraphListener
gradle.getTaskGraph().addTaskExecutionGraphListener(new TaskExecutionGraphListener() {
// 同gradle.taskGraph.whenReady
@Override
void graphPopulated(TaskExecutionGraph taskExecutionGraph) {
println "getTaskGraph().addTaskExecutionGraphListener.graphPopulated"
}
})
其实gradle中的hook函数主要也就那几个,都是构建流程的几个关键节点,只是提供了多种注册Hook的方式,所以对于刚开始学的时候,感觉很乱很杂,建议实际项目中选择其中一种
以上是gradle系列生命周期相关的知识,后续系列会持续更新,如果有写的不对的地方欢迎批评指正,感觉写的还不错的也欢迎评论点赞支持一下