Gradle简单介绍
gradle在Android studio中和project的关系
打开Android studio窗口,一般是一个工程下面有一个主module,和其他相关的依赖module,但是对于gradle来说,其实每一个module都是一个project,每个project下面都需要有一个buid.gradle,即是该project的编译脚本,但是如果针对每一个project都单独编译的话, 想必是个很耗时的工作,而且大多数情况下,各个project 之间也是相互依赖的,所以gradle定义了muti-project build,即是一个project可以连带其附属的子project一起编译,只要在该project目录下建立一个setting.gradle,然后将所有的project在其中include进来即可,同时也可以在项目根目录下建立一个build.grale,配置一些各个project都可以用到的配置信息,这个是非必要的,但是如果要支持mutl-project build,那么必须要有setting.gradle:例如:
include ':app',':securitySdk',':networkSdk'
同时可以在settin.gralde中定义一些函数,执行一些必要工作,例如:
include ':app', ':JavaLibrary'
def initSomething(){
println "initialize something when build"
}
initSomething()
project是有多个Task组成的,当执行gradle project时,其实就是执行project的Task,这些Task会完成相应的工作。
如何查看project下的tasks任务
首先cd 到主工程下,例如工程目录如下:
想要查看app下的task,执行:gradle app,既可以看到:
当然也可以直观的从Android studio右侧工具栏Gradle中点击查看具体的task,例如:
gradle task-name执行任务
从上面可以看出,各个task的工作和名称有着极其紧密的联系,所以如果想要直接执行某一个Task,就可以直接的用"gradle tash-name"去执行了,由于gradle是基于Groovy语言的,所以其实执行这些指令就是调用Groovy的函数而已。
Gradle的工作流程
gradle的工作流程如图:
主要分为3步:
- 初始化阶段,其实就是执行工程目录下的setting.gradle
- Configration 阶段,解析每个project的build.gradle,其内部的任务也会被添加到一个有向图里,用于解决执行过程中的依赖关系,这两个阶段之间,可以加一些定制化的Hook。
- 最后就是执行任务阶段,在gradle XXX中指定的任务,gradle就会将这个XXX任务链上的所有任务都按照依赖顺序执行一遍。
gradle执行时,会将脚本转换成Java对象,Gradle主要有3种对象,不同的对象和不同的脚本文件对应:
- Gradle对象:当执行grade XXX的时候,gradle会从默认的脚本中构建一个gradle对象,整个执行过程,只有一个该对象;
- Project对象:每个build.gralde都会转换成一个project对象
- Settings对象:每一个setting.gradle都会转换成一个Settings对象
Project对象
每一个build.gradle文件都会转换成一个Project对象,对应的是Gradle中的Build Script.Project 包含若干 Tasks。另外,由于 Project 对应具体的工程,所以需要为 Project 加载 所需要的插件,比如为 Java 工程加载 Java 插件。其实,一个 Project 包含多少 Task 往往是 插件决定的。
所以,在 Project 中要做的事情:
- 加载插件;
- 不同插件有不同的行话,即不同的配置。我们要在 Project 中配置好,这样插 件就知道从哪里读取源文件等
- 设置属性。
加载插件
加载插件通过调用appply函数,函数调用时通过 参数名1:参数值2, 参数名2:参数值2的方式来传递参数,例如:
apply plugin:'com.android.library'. //如果是编译library,则加载这个插件
applu plugin:'com.android.application' //如果是Android app,则加载此插件
上面这些插件其实就是下载了相应的jar包,另外也可以加载gradle文件,例如将一些通用函数放到一个utils.gralde中,然后其他过程的gradle来加载这个utils.gradle,然后就可以调用里面的函数了,例如:
apply from:rootProject.getRootDir().getAbsolutePath() +"/utils.gradle"
Task介绍
Task是gradle的一种数据类型,代表了一些要执行或者要干的工作,不同的插件可以添加不同的task,每一个task都需要有一个project关联,具体看下task的例子,在build.gradle中定义:
//Task时和project关联的,所以要利用project的task函数来创建一个Task。
task myTask //新建task
task myTask{ configure closure}
task myType << {task action} // <<是doLast的缩写
task myTask(type:SomeType)
task myTask(type:SomeType){configure closure}
说明:
一个 Task 包含若干 Action。所以,Task 有 doFirst 和 doLast 两个函数,用于 添加需要最先执行的 Action 和需要和需要最后执行的 Action。Action 就是一个闭 包。
- Task 创建的时候可以指定 Type,通过 type:名字表达。这是其实就是告诉 Gradle,这个新建的 Task 对象会从哪个基类 Task 派生。比如,Gradle 本 身提供了一些通用的 Task,最常见的有 Copy 任务。Copy 是 Gradle 中的一个类。当我们:task myTask(type:Copy)的时候,创建的 Task 就是一个 Copy Task。
- 当我们使用 task myTask{ xxx}的时候。花括号是一个 closure。这会导致 gradle 在创建这个 Task 之后,返回给用户之前,会先执行 closure 的内容。
- 当我们使用 task myTask << {xxx}的时候,我们创建了一个 Task 对象,同时把 closure 做为一个 action 加到这个 Task 的 action 队列中,并且告诉它“最后才执行这个 closure”(注意,<<符号是 doLast 的代表)。
gradle实例
1.首先创建一个utils.gradle,用于定义一些公共的函数,以便被其他project引用
def getVersionNameAdvanced(){
def xmlFile = project.file("AndroidManifest.xml")
def rootManifest = new XmlSlurper().parse(xmlFile)
return rootManifest['@android:versionName']
}
//对于library的编译,disable所有的debug编译任务
def disableDeugBuild(){
//project.tasks包含了所有的tasks,findAll就是找到那些名字中带debug的Task,返回值保存到targetTasks容器中
def targetTasks = project.tasks.findAll {
task ->
task.name.contains("Debug")
}
//对于满足条件的task,设置它为disable,这样,这个task就不会被执行
targetTasks.each {
println "disable debug task :${it.name}"
it.setEnabled(false)
}
}
//将函数设置为 extra 属性中去,这样,加载 utils.gradle 的 Project 就能调用此文件中定义的函数了
ext{
getVersionNameAdavanced = this.&getVersionNameAdvanced
disableDebugBuild = this.&disableDeugBuild
}
2.settings.gradle中,调用include把需要包含的子project加进来,示例如下:
include ':app', ':JavaLibrary'
/**
* 这个函数的目的是:
* 1.解析local.properties的文件,读取Android SDK 和NDK的路径
* 2.获取最终产出目录的路径,这样,编译完的apk或者jar包将拷贝到这个最终产出物目录中
* 3.获取Android SDK指定编译的版本
**/
def initEnvironment(){
println "initialize gradle environment start"
Properties properties = new Properties()
File properFile = new File(rootDir.getAbsolutePath()+"/local.properties")
properties.load(properFile.newDataInputStream())
/**
* settings对象创建位于具体的project创建之前,而gradle对象已经创建好了,所以将local.properties信息读出来后,
* 通过extra属性方式设置到gradle对象中,而具体的project执行时,就可以直接从gradle对象中得到这些属性了
*/
gradle.ext.api = properties.getProperty('sdk.api')
gradle.ext.sdkDir = properties.getProperty('sdk.dir')
gradle.ext.ndkDir = properties.getProperty('ndk.dir')
gradle.ext.localDir = properties.getProperty('local.dir')
//指定debug keystore文件位置,debug版apk签名时会用到
gradle.ext.debugKeyStore = properties.getProperty('debug.keystore')
println "initialize gradle environment complete"
}
//初始化
initEnvironment()
3.在local.properties中加入以上定义的参数
ndk.dir=/Users/jkk/Library/Android/sdk/ndk-bundle
sdk.dir=/Users/jkk/Library/Android/sdk
local.dir = /Users/jkk/E/dd-dir
debug.keystore = /Users/jkk/E/mykeystore.jks
sdk.api=android-28
4.再看看根目录下的build.gradle,主要是做一些全局配置
buildscript {
ext.kotlin_version = '1.2.71'
repositories {
google()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.2.1'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
这里其实可以用到好几种闭包,例如allproject{} , buildscript{},configurations{},reposities{},sourceSets{},subprojects{}等等,每个都有不同的含义,例如以下几种:
subprojects:遍历所有的子project,由于其他的闭包都在subprojects,所以其实相当于对每个project都配置了一些信息
buildscript:主要用来执行所依赖的classpath等信息
reposities:定义所以来的仓库