前言
做为一个做Android的同学,我想对于“Gradle”是再熟悉不过了,但是对于Gradle却是有点陌生,几个月前,只是停留在这样简单的使用上。
compile “com.strange.unfamiliar:1.0”
什么让我意识到其重要性呢?应该是在秋招完后,刷刷拉勾,看到杭州某创业公司对应届Andoid 40k的诱惑,赶紧投了一波,投投投,然后收到了面试邀请,刚开始面,感觉这不按套路出牌啊,并不是很偏cs基础,而是在工程实践上,期间问到了gradle的问题,当时内心OS
勉强凑合了几句之后,继续追问,然后全程
面完之后要了面试官的联系方式,进行了一些沟通,意识到了其重要性,然后想着通过项目驱动来深入的学习一波,决定撸QQ空间热修复实现方式中打补丁包的Gradle插件,没错,就是要抄个nvwa。
so,准备出一系列文章来分享整个学习的过程。文章其实是从去年已经开始写了两篇,然后持续delay了,这里重新捡起来。本篇将对Gradle的一些功能,重要性和其一些基础做下讲解,接下来,将进行Android项目中Gradle的讲解,如何在AndroidStudio中使用Gradle来进行一些自定义构建。接着来进行一个简单的插件实现,最后着手来进行hotfix插件的实现,共分为四个部分,希望在对自己的学习做一个总结的同时,能够让零基础入门的Gradle的能够跟随博客对Gradle有个认识,同时能够实现一个简单的Gradle插件。
Gradle是什么?
Gradle是一个基于Apache Ant和Apache Maven概念的项目自动化建构工具。它使用一种基于Groovy的特定领域语言来声明项目设置,而不是传统的XML。
那么Gradle相比于Ant 和 Maven的构建方式,有那些优势呢?
- 自动处理包相依关系 - 取自 Maven Repos 的概念
- 自动处理布署问题 - 取自 Ant 的概念
- 条件判断写法直觉 - 使用 Groovy 语言
过去 Java 开发者常用 Maven 和 Ant 等工具进行封装布署的自动化,或是两者兼用,不过这两个包彼此有优缺点,如果频繁改变相依包版本,使用 Ant 相当麻烦,如果琐碎工作很多,Maven 功能不足,而且两者都使用 XML 描述,相当不利于设计 if、switch 等判段式,即使写了可读性也不佳,而 Gradle 改良了过去 Maven、Ant 带给开发者的问题,至今也成为 Android Studio 内置的封装布署工具。
Android中Gradle可以做什么?
上一篇文章中讲到了一个Android项目的构建过程,Android 构建系统编译应用资源和源代码,然后将它们打包成可供您测试、部署、签署和分发的 APK。Android Studio 使用 Gradle 这一高级构建工具包来自动化执行和管理构建流程,同时也允许您定义灵活的自定义构建配置。每个构建配置均可自行定义一组代码和资源,同时对所有应用版本共有的部分加以重复利用。Android Plugin for Gradle 与这个构建工具包协作,共同提供专用于构建和测试 Android 应用的流程和可配置设置。
Gradle 和 Android 插件独立于 Android Studio 运行。这意味着,可以在 Android Studio 内、使用计算机上的命令行工具或在未安装 Android Studio 的计算机(例如持续性集成服务器)上构建 Android 应用。如果您不使用 Android Studio,可以学习如何从命令行构建和运行您的应用。无论您是从命令行、在远程计算机上还是使用 Android Studio 构建项目,构建的输出都相同。
如上图所示,在一个Project中,除了我们项目自身的代码和资源之外,会有多个与项目构建相关的.gradle文件,这些.Gradle文件用来对于我们使用Gradle进行构建项目的整个过程中来使用。
Gradle中,每一个待编译的工程都叫一个Project。每一个Project在构建的时候都包含一系列的Task。比如一个Android APK的编译可能包含:Java源码编译Task、资源编译Task、JNI编译Task、lint检查Task、打包生成APK的Task、签名Task等。
Gradle工作流程
Gradle的工作流程如下图所示,在每一个工作流程的前后,我们都可以进行一些hook操作,来满足自己的需求。
Gradle工作包含三个阶段:
- 首先是初始化阶段。对我们前面的multi-project build而言,就是执行settings.gradle
- Initiliazation phase的下一个阶段是Configration阶段。
- Configration阶段的目标是解析每个project中的build.gradle。比如multi-project build例子中,解析每个子目录中的build.gradle。在这两个阶段之间,我们可以加一些定制化的Hook。这当然是通过API来添加的。
- Configuration阶段完了后,整个build的project以及内部的Task关系就确定了。一个Project包含很多Task,每个Task之间有依赖关系。Configuration会建立一个有向图来描述Task之间的依赖关系。所以,我们可以添加一个HOOK,即当Task关系图建立好后,执行一些操作。
- 最后一个阶段就是执行任务了。当然,任务执行完后,我们还可以加Hook。
简言之,Gradle有一个初始化流程,这个时候settings.gradle会执行。
在配置阶段,每个Project都会被解析,其内部的任务也会被添加到一个有向图里,用于解决执行过程中的依赖关系。然后才是执行阶段。你在gradle xxx中指定什么任务,gradle就会将这个xxx任务链上的所有任务全部按依赖顺序执行一遍!
Gradle主要有三种对象
这三种对象和三种不同的脚本文件对应,在gradle执行的时候,会将脚本转换成对应的对象:
- Gradle对象:当我们执行gradle xxx或者什么的时候,gradle会从默认的配置脚本中构造出一个Gradle对象。在整个执行过程中,只有这么一个对象。Gradle对象的数据类型就是Gradle。我们一般很少去定制这个默认的配置脚本。
- Project对象:每一个build.gradle会转换成一个Project对象。
- Settings对象:显然,每一个settings.gradle都会转换成一个Settings对象。
构建的生命周期,首先根据settings.gradle文件构建出一个Seetings对象,然后根据Seetings中的配置,创建Project对象,去找各个project下的build.gradle文件,根据文件内容来对project对象进行配置。
一个project中Task的数量,取决于其中应用的插架的数目多少,通过
apply plugin: 'com.android.library'
一个Task包含若干Action。所以,Task有doFirst和doLast两个函数,用于添加需要最先执行的Action和需要和需要最后执行的Action。Action就是一个闭包。对于原有的Task,我们可以在其执行之前或者执行之后,进行一系列的Hook操作,在其执行之前和执行之后,添加一些操作。
tasks.getByName("task"){
it.doLast{
println "do the task"
}
}
Groovy概述
这里将对Groovy语言进行一个简单的介绍,通过简单地语法上的介绍,可以很好地看明白接下来对于构建过程中一些简单地Groovy语法。通过简短的介绍,可以很好地帮助我们看懂Gradle中的一些配置信息。
Groovy是一种动态语言,基于Java并拓展了Java。 Java程序员可以无缝切换到使用Groovy开发程序。Groovy让写Java程序变得像写脚本一样简单。写完就可以执行,Groovy内部会将其编译成Java class然后启动虚拟机来执行。下图是Groovy和Java代码和JVM的关系图。
语言概述
- Groovy中支持动态类型,即定义变量的时候可以不指定其类型。(def不是必须的,但是为了代码清晰,建议还是使用def关键字)
def a = 5;
def b = "groovy"
- 函数的定义,我们也无需进行参数类型的声明,同时也可以不进行返回值类型的声明,但是需要通过def字段来定义,函数的最后一行作为返回值。
def function1(arg1, arg2) {
arg1 + arg2
}
String function2(str1, str2) {
return str1 + str2
}
- 函数调用支持
参数名:参数值
方式调用
apply plugin: 'com.android.library'
plugin:参数名,'com.android.library':参数值
- 强大字符串支持功能
//单引号对应Java中字符串
str1 = 'this string'
//双引号,可通过$进行相应的转译
x = 1
str2 = "This is $x"
//通过换行实现每一行的间距
str3 = '''begin
line1
line2
end'''
- 闭包
(英语:Closure),又称词法闭包(Lexical Closure)或函数闭包(function closures),是引用了自由变量的函数。这个被引用的自由变量将和这个函数一同存在,即使已经离开了创造它的环境也不例外。所以,有另一种说法认为闭包是由函数和与其相关的引用环境组合而成的实体。对于闭包的实现,从函数式编程的角度来看就为了解决一个输入对应一个输出的问题。例如当我们想实现一个加法,我们必须通过传递两个参数来实现,但是借助于函数式编程,我们可以做到只传递一个参数。
function plusAny(first) {
return function(second) {
return first + second;
}
}
var longLiveSeniorFunc = plusAny(1);
longLiveSeniorFunc(1);
- Closure结构定义
def xxx = {paramters -> code}
def xxx = {无参数,纯code}
根据上述两种结构,下面分别举例
def closure = {
String param ->
println "This is $param"
}
def closure = {
println 'This is closure'
}
- 如何调用闭包
closure.call('Hello')
closure('Hello')
闭包隐含一个自身参数it
def closure = {
println "This is $it"
}
当闭包作为一个函数的参数时
def testClosure(Closure closure) {
closure()
}
testClosure(
println 'Test'
)
总结
本文围绕Gradle是什么,可以做什么,在Android中起到了什么作用,然后是Gradle的工作流程,Gradle中使用语言Groovy的一个概述,帮助我们了解如何更好的使用Gradle。
参考文章
深入理解Android(一):Gradle详解