对于Android而言,我们一般依托于AndroidStudio来使用Gradle,
因此这里就不浪费笔墨单独介绍Gradle的下载和配置了。
Gradle是一个编译打包工具,但实际上它也是一个编程框架。
Gradle有自己的API文档,对应链接如下:
https://docs.gradle.org/current/javadoc/org/gradle/api/Project.html
由此可以看出,编写Gradle脚本时,我们实际上就是调用Gradle的API编程。
1 基本组件
Project:
Gradle中,每一个待编译的工程都是一个Project。
例如,用AndroidStudio构建项目时,每一个module都是Gradle定义的Project。
Task:
每一个Project在构建的时候都包含一系列的Task。
比如一个Android APK的编译可能包含:
Java源码编译Task、资源编译Task、JNI编译Task、lint检查Task等。
对于Gradle的编译打包流程而言,Task就是最小的执行单元,
其中将调用具体的函数来完成实际的工作。
Plugin:
一个Project到底包含多少个Task,在很大程度上依赖于编译脚本指定的插件。
插件定义了一系列基本的Task,并指定了这些Task的执行顺序。
整体来看,Gradle作为一个框架,负责定义通用的流程和规则;
根据不同的需求,实际的编译工作则通过具体的插件来完成。
例如,编译Java项目时使用Java插件、编译Groovy项目时使用Groovy插件等。
举个例子来说,我们在Android APK对应的build.gradle中经常可以看到如下代码:
apply plugin: 'com.android.application'这就是使用编译APK的插件。
同样,在编译Android Library时可以看到如下代码,用于指定使用编译Library的插件:
2 具体示例
为了更好理解Gradle中的组件及编译方法,我们来看个例子。
如上图所示,NetworkFramework目录下包含两个Module:
其中一个是Android App,另一个是Android Library。
按照Gradle的定义,这两个Module都是单独的Project。
从上图可以看出,每个Gradle Project在其根目录下都需要一个build.gradle文件。
build.gradle文件就是该Project的编译脚本。
每个Gradle Project都可以独立编译。
具体的做法是:
进入到Project的根目录,然后利用gradle命令执行某个Task。
例如,当我们需要编译app时,只需要进入到app的根目录,
然后执行如下命令(gradle需要添加到环境变量中):
其中,assemble是一个Task的名称,由Android对应的插件定义。
我们可以用如下命令,查看当前插件定义的所有Task:
该命令执行后的效果类似于:
除了单独编译一个Project以外,Gradle还支持多个Project同时编译,即Multi-Projects Build。
还是以上图中的例子来进行说明。
对于AndroidStudio而言,上图是一个Project,包含了两个Module。
对于Gradle而言,这就是一个Multi-Projects。
如上图中的红线所示,Android Project成为Gradle Multi-Projects的关键:
在其根目录定义了build.gradle和settings.gradle文件。
定义build.gradle的后,我们就可以直接在Android Project根目录下执行gradle assemble命令,同时编译app和library。
build.gradle的内容类似于:
settings.gradle则主要定义了根目录下具体有多少个Gradle Project,
其内容类似于:
我们可以利用gradle projects查看Multi-Projects与其子Project的关系,
如下图所示:
显示子Project的数量,完全由settings.gradle来决定。
settings.gradle除了可以include外,还可以设置一些函数。
这些函数会在gradle构建整个工程任务的时候执行,
因此可以在settings做一些初始化工作。
此时,在Multi-Project的根目录执行gradle assemble的结果类似于:
从图中可以看出,settings.gradle中定义的test函数在整个构建开启前就执行了。
3 Gradle工作流程
在本篇博客的最后,我们来看一下Gradle的工作流程:
如上图所示,Gradle的工作流程基本包含三个阶段。
首先是初始化阶段,对于Multi-Project而言,就是执行settings.gradle。
初始化阶段结束后,整个工作流程进入到配置阶段。
配置阶段的目标是解析每个子Project中的build.gradle,
建立一个有向图来描述各个Task之间的依赖关系。
配置阶段完成后,就进入执行阶段了,开始依次运行Task进行实际的工作。
从上图可以看出,我们可以在不同的阶段之间增加一些定制化的Hook,
以便在不同阶段的开始和结束进行一些特殊的操作。
我们在Multi-Projects根目录的build.gradle文件中(还可以在每个子Project的build.gradle中增加Hook),增加上图所示的Hook:
.................gradle.beforeProject {project-> println "beforeProject: " + project}gradle.taskGraph.whenReady { graph-> println "graphWhenReady: " + graph}gradle.buildFinished { result-> println "buildFinished:" + result}然后执行gradle assemble看看对应的输出:
可以看出,初始化阶段完成后,依次对每个子Project执行了beforeProject Hook。
当两个子Project的配置阶段都完成后,才执行了taskGraph.whenReady Hook。
整个编译过程全部完成后,才最后执行了buildFinished Hook。
我们在子Project的build.gradle中增加一些Hook并进行编译:
从图中可以看出,会先形成Multi-Projects整体的Task有向图,
然后才会形成各个子Project的Task有向图(从回调Hook的顺序推断的,实际情况可能需要进一步参考文档确定)。
4 总结
本篇博客我们介绍了Gradle的基本概念及工作流程,
现在我们知道了Gradle的编译其实就是按顺序执行一系列的Task。
那么,我们只需要进一步知道如何定义Task之间的依赖关系,
以及如何使用Groovy定义我们需要的Task,就能定制出我们需要的编译流程了。
这部分内容我们在下一篇博客中继续介绍。