一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第5天,点击查看活动详情。
Gradle 入门
1, DSL全称
Domain Special Language
2,Gradle目录清单
docs:
API、DSL、指南等文档
getting-started.html:
入门链接
init.d:
gradle的初始化脚本目录
lib:
相关库
LICENSE:
media:
一些icon资源
NOTICE:
samples:
示例
src:
源文件
3,Linux配置环境变量
-[3.1] 编辑~/.bashrc文件
GRADLE_HOME=/home/liubo/***
PATH=${PATH}:${GRADLE_HOME}/bin
Export GRADLE_HOME PATH
3.2 在终端输入source ~/.bashrc
备注:如果想让所有用户使用Gradle,需要在/etcc/profile添加上述内容,然后重启计算机
4,Gradle Wrapper
| 参数名 | 说明 |
|---|---|
| --gradle-version | 用于指定使用的Gradle版本 |
| --gradle-distribution-url | 用于指定下载Gradle发行版的url地址 |
使用方法
gradle wrapper --gradle-version 2.4
gradle-wrapper.properties
| 字段名 | 说明 |
|---|---|
| distributionBase | 下载的Gradle压缩包解压后存储的主目录 |
| distributionPath | 相对于distributionBase的解压后的Gradle压缩包的路径 |
| zipStoreBase | 同distributionBase,只不过是存放zip压缩包的 |
| zipStorePath | 同distributionPath,只不过是存放zip压缩包的 |
| distributionUrl | Gradle发行版压缩包的下载地址 |
5,自定义Wrapper Task
gradle-wrapper.properties由Wrapper Task生成。我们也可以自定义Wrapper Task来生成该文件。在build.gradle文件中加入下面的代码
task wrapper(type:Wrapper){
gradleVersion = '2.4'
archiveBase = 'GRADLE_USER_HOME'
archivePath = 'wrapper/dists'
distributionBase = 'GRADLE_USER_HOME'
distributionPath = 'wrapper/dists'
distributionUrl = 'http\://services.gradle.org/distributions/gradle-2.4-all.zip'
}
6,Gradle日志
| 级别 | 用于 |
|---|---|
| ERROR | 错误消息 |
| QUIET | 重要消息 |
| WARNING | 警告消息 |
| LIFECYCLE | 进度消息 |
| INFO | 信息消息 |
| DEBUG | 调试消息 |
使用示例
gradle -i tasks
无选项的时候,输出的日志级别是:LIFECYCLE及更高级别。
错误堆栈开关选项
| 命令行选项 | 用于 |
|---|---|
| 无选项 | 没有堆栈信息输出 |
| -s/--stacktrace | 输出关键性的堆栈信息 |
| -S/--full-stacktrace | 输出全部堆栈信息 |
一般推荐使用-s而不是-S,因为-s精简易读
6,使用日志信息调试
println
println '输出一段日志信息'
内置的logger输出不同级别的日志
logger.error('Message')
logger.quiet('Message')
logger.lifecycle('Message')
logger.info('Message')
logger.debug('Message')
7,Gradle 命令行
帮助
./gradlew -?
./gradlew -h
./gradlew -help
查看所有的任务
tasks
Gradle Help任务
./gradlew help --task
示例
./gradlew help --task tasks
就可以显示tasks任务的帮助信息
强制刷新依赖
./gradlew --refresh-dependencies assemble
8,多任务调用
运行多个任务,只需要__空格隔开即可__
比如在执行jar之前先进行clean
./gradlew clean jar
9,通过任务名缩写执行
任务名比较长的时候,可以通过基于驼峰命名法的缩写调用
./gradlew connectCheck
可以缩写成
./gradlew cc
Groovy基础
1,字符串
单引号不支持字符串的__插值__操作(没有运算能力)
task printStringVar << {
def name = "张三"
println '单引号的变量计算: ${name}'
println "双引号的变量计算: ${name}"
}
./gradlew printStringVar运行后输出:
单引号的变量计算: ${name}
双引号的变量计算: 张三
__备注:__只有一个变量的时候,可以省略{},如$name
2,集合
List
task printList << {
def numList = [1,2,3,4,5,6,7,8,9,0]
println numList[1]//访问第二个元素
println numList[-1]//访问最后一个元素
println numList[-2]//访问倒数第二个元素
println numList[1..3]//访问第二个到第四个元素
//遍历
numList.each {
println it
}
}
Map
task printMap << {
def map1 = ['width':1024,'height':768]
println map1['width']
println map1.height
map1.each {
println "key = ${it.key}, value = ${it.value}"
}
}
对于集合,Groovy还提供了collect、find、findAll等便捷方法。
3,方法
3.1,括号可以省略
task invokeMethod << {
method1(1,2)
//也可以写为
method1 1,2
}
3.2,return 可以不写
Groovy会把方法执行过程中的最后一句代码作为其返回值
3.3,代码块可以作为参数传递 --> 闭包
numList.each ( {println it} )
//Groovy规定,如果方法的最后一个参数是闭包,可以放到方法外面
numList.each(){
println it
}
//括号也可以省略
numList.each {
println it
}
4,JavaBean
5,闭包Closure
示例
task helloClosure << {
customEach {
println it
}
}
def customEach (closure) {
for(int i = 0; i < 10; i ++ ){
closure(i)
}
}
6,DSL
Domain Specific Language 领域特定语言
Martin Fowler的《领域特定语言》
Gradle构建脚本基础
1,Setting 文件
工程根目录下的设置文件,默认是setting.gradle,用于初始化以及工程树的配置
示例
rootProject.name = 'android-gradle-book-code'
//第一章项目例子定义
include ':example02'
project(':example02').projectDir = new File(rootDir, 'chapter01/example02')
include ':example03'
project(':example03').projectDir = new File(rootDir, 'chapter01/example03')
2,Build文件
Project根目录下的build.gradle文件,
3,Projects以及Tasks
-
Project是一个独立的模块
-
Task是一个操作,一个原子性的操作。相当于Ant里的Target,Maven里的goal一样;
4,创建一个任务
方法原型是
create(String name, Closure configureClosure)
task customTask1 {
doFirst {
println 'customTask1: doFirst'
}
doLast {
println 'customTask1: doLast'
}
}
//也可以写成下面这种方式
tasks.create('customTask2') {
doFirst {
println 'customTask2: doFirst'
}
doLast {
println 'customTask2: doLast'
}
}
5,任务依赖
task task1 << {
doLast {
println 'task1'
}
}
task task2 << {
doLast {
println 'task2'
}
}
//单一依赖
task task3 (dependsOn: task1) {
doLast {
println 'task3'
}
}
//多个任务依赖
task multiTask {
dependsOn task1,task2
doLast {
println 'multiTask'
}
}
6,任务之间通过API控制、交互
7,自定义属性
Project和Task都可允许添加自定义属性,通过应用所属对应的ext属性即可添加额外属性。
//自定义一个Project的属性
ext.age = 18
//通过代码块自定义多个属性
ext {
phone = 13212345678
address = '北京'
}
同样可以应用在SourceSet中
sourceSets.all {
ext.resourceDir = null
}
sourceSets {
main {
resourceDir = 'main/res'
}
test {
resourceDir = 'main/res'
}
}
脚本是代码,代码也是脚本
Gradle任务
1,多种方式创建任务
1.1 以任务名创建任务
def Task createTask1 = task(createTask1)
createTask1.doLast {
println 'createTask1'
}
这种方式的创建,其实是调用Project对象中的方法
task(String name) throws InvalidUserDataException
1.2,以一个任务名 + 对该任务配置的Map对象 来创建任务
def Task createTask2 = task(createTask2, group: BasePlugin.BUILD_GROUP)
createTask2.doLast {
println 'createTask2'
}
这种方式的创建,其实是调用Project对象中的方法
Task task(Map<String, ?> args, String name) throws InvalidUserDataException
Map中可用的配置如下
| 配置项 | 描述 | 默认值 |
|---|---|---|
| type | 基于一个存在的Task来创建,和类继承差不多 | DefaultTask |
| overwrite | 是否替换存在的Task,这个和type配合起来用 | false |
| dependsOn | 用于配置任务的依赖 | [] |
| action | 添加到任务中的一个Action或者一个闭包 | null |
| description | 任务的描述 | null |
| group | 任务的分组 | null |
1.3 任务名字 + 闭包
task createTask3 {
description '演示任务创建'
doLast {
println 'createTask3'
}
}
这种方式的创建,其实是调用Project对象中的方法
Task task(String name, Closure configureClosure)
1.4 任务名字 + Map参数 + 闭包
上面几种创建方式,最终都是调用TaskContainer对象的create方法
2,多种方式访问任务
2.1,以访问集合元素的方式访问
task tempTask
tasks['tempTask'].doLast {
println 'tempTask.doLast'
}
该方式实际上调用的是
tasks.getAt('tempTask')
//查看gradle源码的话,会发现最终调用的是findByName(String name)
2.2,通过路径访问(Get 和 find)
task tempTask
tasks['tempTask'].doLast {
println tasks.findByName(':parentPath:tempTask')
println tasks.getByName(':parentPath:tempTask')
println tasks.findByName(':parentPath:bbbb')
}
get 和 find的区别: get的时候,如果找不到该任务,就会抛出UnknownTaskException异常;find在找不到任务的时候返回null
2.3,通过名称访问(Get 和 find )
task tempTask
tasks['tempTask'].doLast {
println tasks.findByName('tempTask')
println tasks.getByName('tempTask')
}
- get和find的区别,同上面一样;
- 方法2和方法3的区别: 路径访问的时候,参数值可以是任务路径,也可以是任务名;通过名称访问的时候,参数值只能是任务名。
3,任务分组和描述
4, << 操作符
在Gradle的Task上是doLast方法的短标记形式。
解析
<< 对应Gradle源码中的leftShift方法,即 a << b对应的是 a.leftShift(b)
该方法内部调用了actions.add()
5,任务的执行分析
def Task myTask = task ex45CustomTask(type: CustomTask)
myTask.doFirst{
println 'Task执行之前执行 in doFirst'
}
myTask.doLast{
println 'Task执行之后执行 in doLast'
}
class CustomTask extends DefaultTask {
//使用Task方法创建任务的时候,Gradle会解析带有TaskAction标注的方法作为其执行的Action,然后通过prependParallelSafeAction方法把该Action添加到actions List中
@TaskAction
def doSelf() {
println 'Task自己本身在执行 in doSelf'
}
}
6,任务排序
shouldRunAfter
taskB.shouldRunAfter(taskA)表示taskB应该在taskA执行之后执行,不过也有可能任务顺序并不会按预设的执行;
mustRunAfter
taskB.shouldRunAfter(taskA)表示taskB必须在taskA执行之后执行
7,任务的启用和禁用
Task有个enabled属性,用于启用和禁用任务,默认是true,表示启用。
task tempTask << {
println 'tempTask'
}
ex47DisenabledTask.enabled =false
8,任务的onlyIf断言
断言是一个条件表达式,接受一个闭包作为参数,如果该闭包返回true,则该任务执行,否则跳过。
示例
final String BUILD_APPS_ALL="all";
final String BUILD_APPS_SHOUFA="shoufa";
final String BUILD_APPS_EXCLUDE_SHOUFA="exclude_shoufa";
task ex48QQRelease << {
println "打应用宝的包"
}
task ex48BaiduRelease << {
println "打百度的包"
}
task ex48HuaweiRelease << {
println "打华为的包"
}
task ex48MiuiRelease << {
println "打Miui的包"
}
task build {
group BasePlugin.BUILD_GROUP
description "打渠道包"
}
build.dependsOn ex48QQRelease,ex48BaiduRelease,ex48HuaweiRelease,ex48MiuiRelease
ex48QQRelease.onlyIf {
def execute = false;
if(project.hasProperty("build_apps")){
Object buildApps = project.property("build_apps")
if(BUILD_APPS_SHOUFA.equals(buildApps)
|| BUILD_APPS_ALL.equals(buildApps)){
execute = true
}else{
execute = false
}
}else{
execute = true
}
execute
}
ex48BaiduRelease.onlyIf {
def execute = false;
if(project.hasProperty("build_apps")){
Object buildApps = project.property("build_apps")
if(BUILD_APPS_SHOUFA.equals(buildApps)
|| BUILD_APPS_ALL.equals(buildApps)){
execute = true
}else{
execute = false
}
}else{
execute = true
}
execute
}
ex48HuaweiRelease.onlyIf {
def execute = false;
if(project.hasProperty("build_apps")){
Object buildApps = project.property("build_apps")
if(BUILD_APPS_EXCLUDE_SHOUFA.equals(buildApps)
|| BUILD_APPS_ALL.equals(buildApps)){
execute = true
}else{
execute = false
}
}else{
execute = true
}
execute
}
ex48MiuiRelease.onlyIf {
def execute = false;
if(project.hasProperty("build_apps")){
Object buildApps = project.property("build_apps")
if(BUILD_APPS_EXCLUDE_SHOUFA.equals(buildApps)
|| BUILD_APPS_ALL.equals(buildApps)){
execute = true
}else{
execute = false
}
}else{
execute = true
}
execute
}
打包命令如下
#打所有渠道包
./gradlew :moduleApp:build
./gradlew -Pbuild_apps=all :moduleApp:build
#打首发包
./gradlew -Pbuild_apps=shoufa :moduleApp:build
#打非首发包
./gradlew -Pbuild_apps=exclude_shoufa :moduleApp:build
备注:
命令中的 -P的意思是,为Project指定K-V格式的属性键值对,使用格式为-PK=V
9,任务规则
Gradle插件
1,插件的作用
- 添加任务到项目中,如测试,编译,打包;
- 添加依赖配置到项目中;
- 向项目中现有对象添加新的扩展属性、方法等,帮助我们配置、优化构建,比如android{}这个配置块就是Android Gradle插件为Project对象添加的一个扩展;
- 可以对项目进行一些约定,比如应用java插件后,约定src/main/java是源码存放位置;