Gradle 入门

898 阅读5分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 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压缩包的
distributionUrlGradle发行版压缩包的下载地址
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是源码存放位置;