栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

《Android Gradle》权威指南笔记

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

《Android Gradle》权威指南笔记

文章目录

Gradle

1. Gradle入门

1.2 gradle版helloWorld1.3 Gradle Wrapper1.4 Gradle 日志

1.4.2 输出错误信息 1.5 Gradle 命令行 2. Groovy基础

2.1 字符串2.2 集合2.3 方法2.4 javaBea2.5 闭包2.6 dsl 3. Gradle 构建脚本基础

3.1 Setting文件3.2 Build文件3.3 projects及tasks3.4 创建一个任务3.5 任务依赖3.6 任务间通过API控制、交互3.7 自定义属性3.8 脚本即代码,代码也是脚本 4. Gradle任务

4.1 Gradle多种方式创建任务4.2 多种方式访问任务4.3 任务分组和描述4.5 任务的执行分析4.6 任务排序4.7 任务的启用和禁用4.8 任务的OnlyIf断言4.9任务规则 5. Gradle插件

5.1 插件的作用5.2 如何引用一个插件5.3 自定义插件

Gradle 1. Gradle入门 1.2 gradle版helloWorld

build.gradle

// 这个构建脚本定义了一个(任务)Task,任务名hello
// doLast意味着在Task执行完毕之后,要回调doLast里的闭包
task hello {
    doLast {
       	// groovy中,单引号和双引号都代表字符串
        println 'hello world'
    }
}

执行:gradle hello

Configure project :
hello world

build.gradle是Gradle默认的构建脚本文件,执行Gradle命令时,会默认加载当前目录下的build.gradle文件

1.3 Gradle Wrapper

Wrapper顾名思义,是对Gradle的一层包装,便于在团队开发时统一Gradle构建的版本。

生成wrapper

wangzhiping@wangzhiping-PC:~/GradleProject$ gradle wrapper

BUILD SUCCESSFUL in 2s
1 actionable task: 1 executed

目录结构

wangzhiping@wangzhiping-PC:~/GradleProject/gradle$ tree
.
└── wrapper
├── gradle-wrapper.jar
└── gradle-wrapper.properties

1.4 Gradle 日志
级别用途
error错误信息
quiet重要信息
warning警告信息
lifecycle进度信息
info信息
debug调试信息
开关选项输出的日志级别
无选项lifecycle及其更高级别
-q 或者 --quietquiet及其更高级别
-i 或者 --infoinfo及其更高级别
-d 或者 --debugdebug及其更高级别,这一般会输出所有日志
1.4.2 输出错误信息

默认情况下,堆栈信息的输出是关闭的,需要通过命令打开。

命令行选项用于
无选项没有堆栈输出
-s 或者–stacktrace输出关键性的堆栈信息
-S 或者 --full-stacktrace输出全部堆栈信息

一般推荐使用-s,-S过于冗余

1.5 Gradle 命令行

1.5.1 使用帮助

gradle -h

1.5.2 查看所有可执行的tasks

gradle tasks

1.5.5 多任务执行

gradle clean hello

多个task用空格隔开

1.5.6 通过任务名缩写执行

例如名为helloWorld的task可以执行为:

wangzhiping@wangzhiping-PC:~/GradleProject$ gradle hW

Task :helloWorld
hello world

2. Groovy基础

Groovy是基于jvm的一种动态语言,完全兼容java,在此基础上又增加了很多动态类型和灵活的特性,比如支持闭包,支持dsl,可以说是一门非常灵活的脚本语言。

2.1 字符串

在groovy中,单引号双引号都可以定义一个字符串常量(java里单引号定义一个字符),不同的是单引号标记的是纯粹的字符串常量,而不是对字符串里的表达式做运算,但是双引号可以

build.gradle

task helloWorld {
    def str1 = 'str1'
    def str2 = "str2"
    println "单引号定义的字符类型:" + str1.getClass().name
    println "双引号定义的字符类型:" + str2.getClass().name
}

wangzhiping@wangzhiping-PC:~/GradleProject$ gradle hW

Configure project :
单引号定义的字符类型:java.lang.String
双引号定义的字符类型:java.lang.String

BUILD SUCCESSFUL in 438ms
<-------------> 0% WAITING

build.gradle

task helloWorld {
    def str1 = 'str1'
    def str2 = "str2"
    println '单引号定义的字符类型:${str1}'
    println "双引号定义的字符类型:${str2}"
}

wangzhiping@wangzhiping-PC:~/GradleProject$ gradle hW

Configure project :
单引号定义的字符类型:${str1}
双引号定义的字符类型:str2

可见,在单引号中不能对字符串里的表达式做运算

2.2 集合

2.1 List

task helloWorld {
    def numList = [1,2,3,4,5]
    println numList.getClass().name
}

wangzhiping@wangzhiping-PC:~/GradleProject$ gradle hW

Configure project :
java.util.ArrayList

可见,numList是一个ArrayList

2.2 打印list

task helloWorld {
    def numList = [1,2,3,4,5]
    println numList.get(0) // 访问第一个元素
    println numList[0] // 访问第一个元素
    println numList[-2] // 访问倒数第二个元素
    println numList[1..3] // 访问第2-4个元素
}

wangzhiping@wangzhiping-PC:~/GradleProject$ gradle hW

Configure project :
1
1
4
[2, 3, 4]

制造异常

task helloWorld {
    def numList = [1,2,3,4,5]
    println numList[-20] // 抛出异常
}

wangzhiping@wangzhiping-PC:~/GradleProject$ gradle -stacktrace hW

Caused by: java.lang.ArrayIndexOutOfBoundsException: Negative array index [-20] too large for array size 5
at build_atjutgku0sx4akor6tqhpkg7l r u n c l o s u r e 1. d o C a l l ( / h o m e / w a n g z h i p i n g / G r a d l e P r o j e c t / b u i l d . g r a d l e : 5 ) a t o r g . g r a d l e . u t i l . i n t e r n a l . C l o s u r e B a c k e d A c t i o n . e x e c u t e ( C l o s u r e B a c k e d A c t i o n . j a v a : 72 ) a t o r g . g r a d l e . u t i l . i n t e r n a l . C o n f i g u r e U t i l . c o n f i g u r e T a r g e t ( C o n f i g u r e U t i l . j a v a : 155 ) a t o r g . g r a d l e . u t i l . i n t e r n a l . C o n f i g u r e U t i l . c o n f i g u r e S e l f ( C o n f i g u r e U t i l . j a v a : 131 ) a t o r g . g r a d l e . a p i . i n t e r n a l . A b s t r a c t T a s k . c o n f i g u r e ( A b s t r a c t T a s k . j a v a : 669 ) a t o r g . g r a d l e . a p i . D e f a u l t T a s k . c o n f i g u r e ( D e f a u l t T a s k . j a v a : 309 ) a t o r g . g r a d l e . a p i . i n t e r n a l . p r o j e c t . D e f a u l t P r o j e c t . t a s k ( D e f a u l t P r o j e c t . j a v a : 1275 ) a t o r g . g r a d l e . i n t e r n a l . m e t a o b j e c t . B e a n D y n a m i c O b j e c t _run_closure1.doCall(/home/wangzhiping/GradleProject/build.gradle:5) at org.gradle.util.internal.ClosureBackedAction.execute(ClosureBackedAction.java:72) at org.gradle.util.internal.ConfigureUtil.configureTarget(ConfigureUtil.java:155) at org.gradle.util.internal.ConfigureUtil.configureSelf(ConfigureUtil.java:131) at org.gradle.api.internal.AbstractTask.configure(AbstractTask.java:669) at org.gradle.api.DefaultTask.configure(DefaultTask.java:309) at org.gradle.api.internal.project.DefaultProject.task(DefaultProject.java:1275) at org.gradle.internal.metaobject.BeanDynamicObject r​unc​losure1.doCall(/home/wangzhiping/GradleProject/build.gradle:5)atorg.gradle.util.internal.ClosureBackedAction.execute(ClosureBackedAction.java:72)atorg.gradle.util.internal.ConfigureUtil.configureTarget(ConfigureUtil.java:155)atorg.gradle.util.internal.ConfigureUtil.configureSelf(ConfigureUtil.java:131)atorg.gradle.api.internal.AbstractTask.configure(AbstractTask.java:669)atorg.gradle.api.DefaultTask.configure(DefaultTask.java:309)atorg.gradle.api.internal.project.DefaultProject.task(DefaultProject.java:1275)atorg.gradle.internal.metaobject.BeanDynamicObjectmetaClassAdapter.invokeMethod(BeanDynamicObject.java:484)
at org.gradle.internal.metaobject.BeanDynamicObject.tryInvokeMethod(BeanDynamicObject.java:196)
at org.gradle.internal.metaobject.CompositeDynamicObject.tryInvokeMethod(CompositeDynamicObject.java:98)
at org.gradle.internal.extensibility.MixInClosurePropertiesAsMethodsDynamicObject.tryInvokeMethod(MixInClosurePropertiesAsMethodsDynamicObject.java:34)
at org.gradle.groovy.scripts.Basicscript script D y n a m i c O b j e c t . t r y I n v o k e M e t h o d ( B a s i c script . j a v a : 135 ) a t o r g . g r a d l e . i n t e r n a l . m e t a o b j e c t . A b s t r a c t D y n a m i c O b j e c t . i n v o k e M e t h o d ( A b s t r a c t D y n a m i c O b j e c t . j a v a : 163 ) a t o r g . g r a d l e . g r o o v y . script s . B a s i c script . i n v o k e M e t h o d ( B a s i c script . j a v a : 84 ) a t b u i l d a t j u t g k u 0 s x 4 a k o r 6 t q h p k g 7 l . r u n ( / h o m e / w a n g z h i p i n g / G r a d l e P r o j e c t / b u i l d . g r a d l e : 1 ) a t o r g . g r a d l e . g r o o v y . script s . i n t e r n a l . D e f a u l t script R u n n e r F a c t o r y scriptDynamicObject.tryInvokeMethod(Basicscript.java:135) at org.gradle.internal.metaobject.AbstractDynamicObject.invokeMethod(AbstractDynamicObject.java:163) at org.gradle.groovy.scripts.Basicscript.invokeMethod(Basicscript.java:84) at build_atjutgku0sx4akor6tqhpkg7l.run(/home/wangzhiping/GradleProject/build.gradle:1) at org.gradle.groovy.scripts.internal.DefaultscriptRunnerFactory ScriptDynamicObject.tryInvokeMethod(BasicScript.java:135)atorg.gradle.internal.metaobject.AbstractDynamicObject.invokeMethod(AbstractDynamicObject.java:163)atorg.gradle.groovy.scripts.BasicScript.invokeMethod(BasicScript.java:84)atbuilda​tjutgku0sx4akor6tqhpkg7l.run(/home/wangzhiping/GradleProject/build.gradle:1)atorg.gradle.groovy.scripts.internal.DefaultScriptRunnerFactoryscriptRunnerImpl.run(DefaultscriptRunnerFactory.java:91)
… 152 more

增加-stacktrace打印堆栈信息,可以看到是index溢出异常

遍历list

task helloWorld {
    def numList = [1,2,3,4,5]
    numList.each {
        println it
    }
}

wangzhiping@wangzhiping-PC:~/GradleProject$ gradle -stacktrace hW

Configure project :
1
2
3
4
5

2.2.2 map

task helloWorld {
    def map = ['width':1024, 'height':768]
    println map.getClass().name
}

wangzhiping@wangzhiping-PC:~/GradleProject$ gradle -stacktrace hW

Configure project :
java.util.linkedHashMap

取map的key或者value

task helloWorld {
    def map = ['width':1024, 'height':768]
    println map.getClass().name

    println map['width']
    println map.height
}

wangzhiping@wangzhiping-PC:~/GradleProject$ gradle -stacktrace hW

Configure project :
java.util.linkedHashMap
1024
768

遍历map

task helloWorld {
    def map = ['width':1024, 'height':768]

    map.each{
        println it.key
        println it.value
    }
}

wangzhiping@wangzhiping-PC:~/GradleProject$ gradle -stacktrace hW

Configure project :
width
1024
height
768

2.3 方法

2.3.1 括号是可以省略的

task helloWorld {
    method1(1,2)
    method1 1,2
}

def method1(int a, int b) {
    println a+b
}

wangzhiping@wangzhiping-PC:~/GradleProject$ gradle hW

Configure project :
3
3

2.3.2 return是可以不写的

task helloWorld {
    println method1(1,2)
}

static def method1(int a, int b) {
    if (a > b) {
        a
    } else {
        b
    }
}

wangzhiping@wangzhiping-PC:~/GradleProject$ gradle hW

Configure project :
2

groovy取最后一行作为返回值,可以不写return

代码块可以作为参数传递

类似于kotlin的闭包优化规则

task helloWorld {
    def numList1 = [1,2,3,4,5]
    numList1.each({println it})

    // 只有一个参数时,可以把闭包提出来
    def numList2 = [1,2,3,4,5]
    numList2.each(){println it}

    // 括号内没有参数时,可以把括号去掉
    def numList3 = [1,2,3,4,5]
    numList3.each{
        println it
    }
}
2.4 javaBea

demo

task helloWorld {
    Person p = new Person()

    println "名字是: ${p.name}"
    p.name = "张三"
    println "名字是: ${p.name}"
}

class Person {
    private String name
}

​ wangzhiping@wangzhiping-PC:~/GradleProject$ gradle hW

Configure project :
名字是: null
名字是: 张三

2.5 闭包

闭包是groovy一个非常重要的特性,可以说是dsl的基础。

2.5.1 初识闭包

task helloWorld {
    customEach {
        println it
    }
}

// 参数名closure是自定义的,可以随便起
def customEach(closure) {
    for (int i in 1..10) {
        closure(i)
    }
}

wangzhiping@wangzhiping-PC:~/GradleProject$ gradle hW

Configure project :
1
2
3
4
5
6
7
8
9
10

2.5.2 向闭包传递参数

task helloWorld {
    customEach { k,v ->
        println "key:" + k + ",value:" + v
    }
}

def customEach(closure) {
    def map = ["name":"张三", "age":18]
    map.each {
        closure(it.key, it.value)
    }
}

wangzhiping@wangzhiping-PC:~/GradleProject$ gradle hW

Configure project :
key:name,value:张三
key:age,value:18

2.5.3闭包委托

task helloWorld {
    new Delegate().test {
        println "thisObject: ${thisObject.getClass()}"
        println "owner: ${owner.getClass()}"
        println "delegate: ${delegate.getClass()}"
        method1()
        it.method1()
    }
}

def method1() {
    println "context this : ${this.getClass()} in root"
    println "method1 in root"
}

class Delegate {
    def method1() {
        println "context this : ${this.getClass()} in delegate"
        println "method1 in delegate"
    }

    def test(Closure closure) {
        closure(this)
    }
}

wangzhiping@wangzhiping-PC:~/GradleProject$ gradle -stacktrace hW

Configure project :
thisObject: class build_atjutgku0sx4akor6tqhpkg7l
owner: class build_atjutgku0sx4akor6tqhpkg7l r u n c l o s u r e 1 d e l e g a t e : c l a s s b u i l d a t j u t g k u 0 s x 4 a k o r 6 t q h p k g 7 l _run_closure1 delegate: class build_atjutgku0sx4akor6tqhpkg7l r​unc​losure1delegate:classbuilda​tjutgku0sx4akor6tqhpkg7l_run_closure1
context this : class build_atjutgku0sx4akor6tqhpkg7l in root
method1 in root
context this : class Delegate in delegate
method1 in delegate

thisObject的优先级最高,默认情况下使用thisObject来处理闭包中调用的方法,如果有则执行。thisObject其实就是这个构建脚本的上下文,它和脚本中的this对象是相等的。从例子中也证明了delegate和owner是相等的(owner: class build_atjutgku0sx4akor6tqhpkg7l r u n c l o s u r e 1 d e l e g a t e : c l a s s b u i l d a t j u t g k u 0 s x 4 a k o r 6 t q h p k g 7 l _run_closure1 delegate: class build_atjutgku0sx4akor6tqhpkg7l r​unc​losure1delegate:classbuilda​tjutgku0sx4akor6tqhpkg7l_run_closure1)。它们两个的优先级是:owner > delegate高,所以闭包内的方法处理顺序是thisObject > owner > delegate

在dsl中,我们一般指定delegate为当前的it,这样就可以在闭包内对该it进行配置,或者调用其方法:

task helloWorld {
    person {
        // 可以操作person对象属性,访问person对象的方法
        personName = "张三"
        personAge = 20
        dumpPerson()
    }
}

class Person {
    String personName
    int personAge

    def dumpPerson() {
        println "name is ${personName}, age is ${personAge}"
    }
}

def person(Closure closure) {
    Person p = new Person()
    closure.delegate = p
    closure.setResolveStrategy(Closure.DELEGATE_FIRST)
    closure(p)
}

拿一段安卓项目的build.gradle文件对比

build.gradle

defaultConfig {
    applicationId "com.example.myapplication"
    minSdk 21
    targetSdk 32
    versionCode 1
    versionName "1.0"

    testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}

// demo
person {
    personName = "张三"
    personAge = 20
    dumpPerson()
}
2.6 dsl

dsl(Domain Specific Language),专门关注某一领域的语言,对比java这种通用全面的语言。

gradle就是一门dsl,它基于groovy,专门解决自动化构建的dsl。

3. Gradle 构建脚本基础 3.1 Setting文件

在Gradle中,定义了一个设置文件,用于初始化以及工程树的配置,设置文件的默认名字是setting.gradle,放在根目录工程下。

设置文件大多数的作用是为了配置子工程,在gradle中多工程是通过工程树表示的,相当于在android studio中看到的project和module一样,根工程相当于android studio中的project,一个根工程可以有很多子工程,也就是很多的module。

一个子工程只有在setting里设置了gradle才会去识别,才会在构建的时候被包含进去。

setting.gradle

rootProject.name = "My Application"
include ':app'

对应于目录结构:

MyApplication/
├── app

3.2 Build文件

每个project都会有一个Build文件,该文件是该project构建的入口,可以在这里针对project进行配置,比如配置版本,需要哪些插件,依赖哪些库等。

既然每个project都会有一个build文件,那么root project也不例外。root project可以获得所有child project,所以可以在root project的build文件里对child project统一配置,比如应用的插件,依赖的maven中心库等。

build.gradle

subprojects {
    repositories {
        jcenter()
    }
}

对所有子project配置maven仓库,制定为jcenter

3.3 projects及tasks

多个project组成整个gradle的构建,一个project又包含多个task,task是一个原子操作,比如打个jar包,复制一份文件,编译一次java代码。

3.4 创建一个任务

task其实是Project对象的一个函数,原型为create(String name, Closure configureClosure)
参数1:任务的名字,可以自定义
参数2:一个闭包,也就是花括号内的代码块

写法1

task helloWorld {
    doFirst {
        println 'customTask:doFirst'
    }
    doLast {
        println 'customTask:doLast'
    }
}

写法2

tasks.create("customTask") {
    doFirst {
        println 'customTask:doFirst'
    }
    doLast {
        println 'customTask:doLast'
    }
}

wangzhiping@wangzhiping-PC:~/GradleProject$ gradle customTask

Task :customTask
customTask:doFirst
customTask:doLast

两种写法的效果是一样的

3.5 任务依赖

使用dependsOn:可以指定多个依赖任务作为参数,dependsOn是Task类的一个方法。

task之间是有依赖关系的,这样我们就可以控制哪些任务优先于哪些任务执行。比如执行jar任务之前,compile任务一定要先执行过,android的install任务一定要依赖package任务打包生成apk。

ex35Hello

task ex35Hello {
    println 'hello'
}

task ex35Main(dependsOn: ex35Hello) {
    doLast {
        println 'Main'
    }
}

通过dependsOn:指定依赖的任务ex35Hello,运行结果:

wangzhiping@wangzhiping-PC:~/GradleProject$ gradle ex35Main

Configure project :
hello

Task :ex35Main
Main

指定多个依赖task

task ex35Hello {
    println "hello"
}

task ex35World {
    println "World"
}

task ex35MultiTask {
    dependsOn ex35Hello,ex35World
    doLast {
        println "multiTask"
    }
}

wangzhiping@wangzhiping-PC:~/GradleProject$ gradle ex35MultiTask

Configure project :
hello
World

Task :ex35MultiTask
multiTask

3.6 任务间通过API控制、交互

创建一个任务和定义一个变量是一样的,变量名就是任务名,类型是Task,所以我们可以通过任务名,使用Task的API访问它的方法、属性或者重新配置等。

ex35Hello

task ex35Hello {
    println "hello"
}

ex35Hello.doFirst {
    println "doFirst"
}

ex35Hello.doLast {
    println "doLast"
}

wangzhiping@wangzhiping-PC:~/GradleProject$ gradle ex35Hello

Configure project :
hello

Task :ex35Hello
doFirst
doLast

判断是否有ex35Hello这个变量

task ex35Hello {
    println "hello"
}

ex35Hello.doFirst {
    println "doFirst"
}

ex35Hello.doLast {
    println "has property ${project.hasProperty('ex35Hello')}"
    println "doLast"
}

wangzhiping@wangzhiping-PC:~/GradleProject$ gradle ex35Hello

Configure project :
hello

Task :ex35Hello
doFirst
has property true
doLast

project.hasProperty(‘ex35Hello’)运行结果是true,说明每个task都是project的一个属性

3.7 自定义属性

project和task都允许用户添加额外的自定义属性,要添加额外的属性,通过应用所属对应的ext属性即可实现。添加之后可以通过ext属性对自定义属性读取和设置,如果要同时添加多个自定义属性,可以通过ext代码块。

ext一般用来自定义版本号名称,把版本号和版本名单独放在一个gradle文件中,便于管理。

ex37CustomProperty

ext.age = 18

ext {
    phone = 122222
    address = "xxxddd"
}

task ex37CustomProperty {
    println "年龄是 ${age}"
    println "电话是 ${phone}"
    println "地址是 ${address}"
}

wangzhiping@wangzhiping-PC:~/GradleProject$ gradle ex37CustomProperty

Configure project :
年龄是 18
电话是 122222
地址是 xxxddd

3.8 脚本即代码,代码也是脚本

虽然我们在gradle文件中写脚本,但是我们写的都是代码,这一点要记住,这样才能时刻使用groovy,java以及gradle的任何语法和api帮你完成想做的事情。是脚本吗?是,但并不是简单的脚本,这脚本上可以定义class、内部类、导入包、定义方法等。

给打包的apk定义生成的文件名

android {
    android.applicationVariants.all { variant ->
        variant.outputs.all {
            outputFileName = "my_${buildTime()}.apk"
        }
    }
}

def buildTime() {
    def date = new Date()
    def formattedDate = date.format('yyyyMMdd')
    return formattedDate
}
4. Gradle任务 4.1 Gradle多种方式创建任务

    直接以任务名字创建

    def Task helloWorld = task(helloWorld)
    
    helloWorld.doLast {
        println "helloooo"
    }
    

    该方法完整的定义是:Task task(String name) throws InvalidUserDataExceptions

    任务+一个对该任务配置的map对象来创建

    def Task helloWorld = task(helloWorld, group:basePlugin.BUILD_GROUP)
    
    helloWorld.doLast {
        println "helloooo"
        println "任务分组${helloWorld.group}"
    }
    

    该函数的原型是:Task task(Map args, String name) throws InvalidUserDataException

    map可配置的参数如下:

    配置项描述默认值
    type基于一个存在的task来创建,和继承差不多DefaultTask
    overwrite是否替换存在的task,和type配合使用false
    dependsOn用于配制任务的依赖[]
    action添加到任务中的一个action或者一个闭包null
    description用于配制任务的描述null
    group用于配制任务的分组null

    任务名+闭包

    task ex41CreateTask {
        description '演示'
        doLast {
            println "创建方法的原型为 : Task task(String name, Closure configureClosure)"
            println "任务描述, ${description}"
        }
    }
    

task原型

 public interface Project extends Comparable, ExtensionAware, PluginAware {
 
	Task task(String var1) throws InvalidUserDataException;

    Task task(Map var1, String var2) throws InvalidUserDataException;

    Task task(Map var1, String var2, Closure var3);

    Task task(String var1, Closure var2);

    Task task(String var1, Action var2);
	...    
}
4.2 多种方式访问任务

    首先,我们创建的任务都会作为项目的一个属性,属性名就是任务名,所以可以直接通过任务名访问和操纵该任务

    task ex41CreateTask
    
    ex41CreateTask.doLast {
        println "hello world"
    }
    

    其次,任务都是通过taskContainer创建的,其实taskContainer就是我们创建任务的集合,在project中,可以通过tasks属性访问taskContainer,所以我们可以以访问集合的方式创建我们的任务

    task ex41CreateTask
    
    tasks['ex41CreateTask'].doLast {
        println "ex41CreateTask doLast"
    }
    

    Task :app:ex41CreateTask
    ex41CreateTask doLast

    这里的[]指的不是map,而是a.getAt(b),对应的例子tasks[‘ex41CreateTask’]就是调用了tasks.getAt(‘ex41CreateTask’)

    通过路径访问

    通过路径访问有两种方式

      get路径访问

      get的时候如果找不到该任务,会抛出UnknownTaskException异常

      task ex41CreateTask
      
      tasks['ex41CreateTask'].doLast {
          println tasks.getByPath(':app:ex41CreateTask')
      }
      

      Task :app:ex41CreateTask
      task ‘:app:ex41CreateTask’

      find路径访问

      find的时候如果找不到任务,返回null

      task ex41CreateTask
      
      tasks['ex41CreateTask'].doLast {
          println tasks.findByPath('ex41CreateTask')
      }
      

      Task :app:ex41CreateTask
      task ‘:app:ex41CreateTask’

      通过路径访问时,参数值可以是任务路径也可以是任务的名字。

      通过名字访问时,参数值只能是名字不能是路径。

4.3 任务分组和描述

任务的分组就是对任务的分类,便于我们对任务进行归类整理。

任务的描述就是说明这个任务的作用。

添加分组和描述

def Task myTask = task ex43GroupTask
myTask.group = basePlugin.BUILD_GROUP
myTask.description = '这是一个构建的引导任务'

myTask.doLast {
    println "group ${group}, descrption:${description}"
}

./gradlew tasks

Build tasks

assemble - Assemble main outputs for all the variants.
assembleAndroidTest - Assembles all the Test applications.
build - Assembles and tests this project.
buildDependents - Assembles and tests this project and all projects that depend on it.
buildNeeded - Assembles and tests this project and all projects it depends on.
bundle - Assemble bundles for all the variants.
clean - Deletes the build directory.
cleanBuildCache - Deletes the build cache directory.
compileDebugAndroidTestSources
compileDebugSources
compileDebugUnitTestSources
compileReleaseSources
compileReleaseUnitTestSources
ex43GroupTask - 这是一个构建的引导任务

4.5 任务的执行分析

当我们执行tasks的时候,其实就是执行其拥有的actions列表,这个列表保存在task对象实例中actions成员变量中,其类型是一个list

private List actions = new ArrayList();

现在我们把task之前执行、task本身执行以及task之后执行分别称为doFirst、doSelf以及doLast,举个例子

// 创建了task,所以task里的actions有了元素
def Task myTask = task ex45CustomTask(type : CustomTask)

// 把doFirst这个action放在actions开头
myTask.doFirst {
    println "task执行之前执行do first"
}

// 把doLast这个action放在actions末尾
myTask.doLast {
    println "task执行之后执行do last"
}

class CustomTask extends DefaultTask {
    @TaskAction
    def doSelf() {
        println "task 自己本身在执行in doSelf"
    }
}

Task :app:ex45CustomTask
task执行之前执行do first
task 自己本身在执行in doSelf
task执行之后执行do last

AbstractTask

public abstract class AbstractTask implements TaskInternal, DynamicObjectAware {
  // 执行task时,就是执行task中该actions集合
  private List actions;	 
  
  public Task doFirst(final String actionName, final Action action) {
        this.hasCustomActions = true;
        if (action == null) {
            throw new InvalidUserDataException("Action must not be null!");
        } else {
            this.taskMutator.mutate("Task.doFirst(Action)", new Runnable() {
                public void run() {
                    // 在list最前面添加
                    AbstractTask.this.getTaskActions().add(0, AbstractTask.this.wrap(action, actionName));
                }
            });
            return this;
        }
    }
  
      public Task doLast(final String actionName, final Action action) {
        this.hasCustomActions = true;
        if (action == null) {
            throw new InvalidUserDataException("Action must not be null!");
        } else {
            this.taskMutator.mutate("Task.doLast(Action)", new Runnable() {
                public void run() {
                    // 在list最后面添加
                    AbstractTask.this.getTaskActions().add(AbstractTask.this.wrap(action, actionName));
                }
            });
            return this;
        }
    }
}

当我们使用Task方法创建ex45CustomTask这个任务时,Gradle会解析所有其带有TaskAction标注的方法作为其Task执行的Action,然后通过Task的prependParallelSafeAction方法把该Action添加到actions List里。

    public void prependParallelSafeAction(Action action) {
        if (action == null) {
            throw new InvalidUserDataException("Action must not be null!");
        } else {
            this.getTaskActions().add(0, this.wrap(action));
        }
    }
4.6 任务排序

mustRunAfter

task order1 {
    println "order1"

    doFirst {
        println "order1 doFirst"
    }
}

task order2 {
    println "order2"

    doFirst {
        println "order2 doFirst"
    }
}

order1.mustRunAfter order2

Configure project :
order1
order2

Task :order2 // order2先执行
order2 doFirst

Task :order1
order1 doFirst // order1后执行

BUILD SUCCESSFUL in 393ms
2 actionable tasks: 2 executed

去掉order1.mustRunAfter order2

task order1 {
    println "order1"

    doFirst {
        println "order1 doFirst"
    }
}

task order2 {
    println "order2"

    doFirst {
        println "order2 doFirst"
    }
}

Configure project :
order1
order2

Task :order1
order1 doFirst

Task :order2
order2 doFirst

order1比order2先执行

或者使用shouldRunAfter,但有可能还是按原顺序执行

shouldRunAfter

task order1 {
    println "order1"

    doFirst {
        println "order1 doFirst"
    }
}

task order2 {
    println "order2"

    doFirst {
        println "order2 doFirst"
    }
}

order1.shouldRunAfter order2

Configure project :
order1
order2

Task :order2
order2 doFirst

Task :order1
order1 doFirst

4.7 任务的启用和禁用

task.enabled

order1.enabled = false

开启enabled = false,没有执行task1

wangzhiping@wangzhiping-PC:~/GradleProject$ gradle order1

Configure project :
order1
order2

关闭enabled,成功执行了task1

BUILD SUCCESSFUL in 394ms
wangzhiping@wangzhiping-PC:~/GradleProject$ gradle order1

Configure project :
order1
order2

Task :order1
order1 doFirst

4.8 任务的OnlyIf断言

断言就是一个条件表达式,Task有一个oflyIf方法,它接受一个闭包作为参数,如果该闭包返回true则该任务执行,否则跳过。

渠道打包举例

final String BUILD_APPS_ALL = "all"
final String BUILD_APPS_SHOUFA = "shoufa"
final String BUILD_APPS_EXCLUDE_SHOUFA = "exclude_shoufa"

task ex48QQRelease {
    println "打应用宝的包"
    doFirst {
        println "打应用宝的包 doFirst"
    }
}

task ex48BaiduRelease {
    println "打百度的包"
    doFirst {
        println "打百度的包 doFirst"
    }
}

task ex48HuaweiRelease {
    println "打华为的包"
    doFirst {
        println "打华为的包 doFirst"
    }
}

task ex48MiuiRelease {
    println "打小米的包"
    doFirst {
        println "打小米的包 doFirst"
    }
}

task build {
    group basePlugin.BUILD_GROUP
    description "打渠道包"
    doFirst {
        println "打渠道包 doFirst"
    }
}

build.dependsOn ex48BaiduRelease,ex48HuaweiRelease,ex48MiuiRelease,ex48QQRelease

ex48BaiduRelease.onlyIf {
    def execute = false
    if (project.hasProperty("build_apps")) {
        Object buildApps = project.property("build_apps")
        if (BUILD_APPS_SHOUFA == buildApps || BUILD_APPS_ALL == buildApps) {
            execute = true
        } else {
            execute = false
        }
    } else {
        execute = true
    }
    execute
}

ex48QQRelease.onlyIf {
    def execute = false
    if (project.hasProperty("build_apps")) {
        Object buildApps = project.property("build_apps")
        if (BUILD_APPS_SHOUFA == buildApps || BUILD_APPS_ALL == 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 == buildApps || BUILD_APPS_ALL == 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 == buildApps || BUILD_APPS_ALL == buildApps) {
            execute = true
        } else {
            execute = false
        }
    } else {
        execute = true
    }
    execute
}

wangzhiping@wangzhiping-PC:~/GradleProject$ gradle -Pbuild_apps=all build

Configure project :
打应用宝的包
打百度的包
打华为的包
打小米的包

Task :ex48BaiduRelease
打百度的包 doFirst

Task :ex48HuaweiRelease
打华为的包 doFirst

Task :ex48MiuiRelease
打小米的包 doFirst

Task :ex48QQRelease
打应用宝的包 doFirst

Task :build
打渠道包 doFirst

wangzhiping@wangzhiping-PC:~/GradleProject$ gradle -Pbuild_apps=shoufa build

Configure project :
打应用宝的包
打百度的包
打华为的包
打小米的包

Task :ex48BaiduRelease
打百度的包 doFirst

Task :ex48QQRelease
打应用宝的包 doFirst

Task :build
打渠道包 doFirst

打包命令为gradle -Pbuild_apps=shoufa build时,比gradle -Pbuild_apps=all build,少执行了小米和华为两个打包任务,应为它们的onlyIf表达式返回false

4.9任务规则

我们创建的任务都在TaskContainer里,由其进行管理。所以当我们访问任务的时候都是通过TaskContainer进行访问,二TaskContainer又是一个NamedDomainObjectCollection,所以说我们的任务规则是NamedDomainObjectCollection的规则。

NamedDomainObjectCollection是一个具有唯一名字的域对象的集合,它里面所有的元素都有一个唯一不变的名字,该名字是String类型,所以我们可以通过名字获取该元素,比如我们通过任务名获取该任务

我们提供的任务名在NamedDomainObjectCollection中可能并不存在,这时候就会调用我们添加的规则来处理这种异常情况。如理:

addRule

tasks.addRule("对该规则的一个描述,便于调试") { String taskName ->
    println "addRule 开始执行"
    task(taskName) {
        println "该${taskName}不存在,请查证后再执行"
    }
}

task ex49RuleTask {
    println "ex49RuleTask 开始执行"
    dependsOn missTask
}

wangzhiping@wangzhiping-PC:~/GradleProject$ gradle ex49RuleTask

Configure project :
ex49RuleTask 开始执行
addRule 开始执行
该missTask不存在,请查证后再执行

如果不在addRule中创建task,则程序崩溃

tasks.addRule("对该规则的一个描述,便于调试") { String taskName ->
    println "addRule 开始执行"
}

task ex49RuleTask {
    println "ex49RuleTask 开始执行"
    dependsOn missTask
}

wangzhiping@wangzhiping-PC:~/GradleProject$ gradle ex49RuleTask

Configure project :
ex49RuleTask 开始执行
addRule 开始执行

FAILURE: Build failed with an exception.

Where:
Build file ‘/home/wangzhiping/GradleProject/build.gradle’ line: 7

What went wrong:
A problem occurred evaluating root project ‘GradleProject’.

Could not get unknown property ‘missTask’ for task ‘:ex49RuleTask’ of type org.gradle.api.DefaultTask.

Try:

Run with --stacktrace option to get the stack trace.
Run with --info or --debug option to get more log output.
Run with --scan to get full insights.

Get more help at https://help.gradle.org

BUILD FAILED in 401ms

如果依赖的task存在,addRule回调不会执行

tasks.addRule("对该规则的一个描述,便于调试") { String taskName ->
    println "addRule 开始执行"
}

task missTask {
    println "missTask 开始执行"
}

task ex49RuleTask {
    println "ex49RuleTask 开始执行"
    dependsOn missTask
}

wangzhiping@wangzhiping-PC:~/GradleProject$ gradle ex49RuleTask

Configure project :
missTask 开始执行
ex49RuleTask 开始执行

函数原型

public class DefaultNamedDomainObjectCollection extends DefaultDomainObjectCollection implements NamedDomainObjectCollection, MethodMixIn, PropertyMixIn {

    public Rule addRule(Rule rule) {
        this.rules.add(rule);
        return rule;
    }
    ...
}

findByName

public T findByName(String name) {
    T value = this.findByNameWithoutRules(name);
    if (value != null) {
        return value;
    } else {
        ProviderInternal provider = this.index.getPending(name);
        if (provider != null) {
            provider.getOrNull();
            return this.index.get(name);
        } else {
            // 要执行的task不存在时,执行applyRules()
            return !this.applyRules(name) ? null : this.findByNameWithoutRules(name);
        }
    }
}

从findByName中可以看出,如果依赖的任务存在,findByName会直接返回,不存在会执行rules

5. Gradle插件

Gradle本身提供一些基本的概念和整体核心框架,其他用于描述真实使用场景逻辑的都以插件扩展,这样的设计可以抽象的方式提供一个核心框架,其他具体的功能和业务都通过插件的扩展的方式来实现,比如构建java应用,就是通过java插件来实现的。

5.1 插件的作用
    添加任务到项目中,帮助完成一些事情,比如测试、编译、打包。可以添加依赖配置到项目中,我们可以通过它们配置我们项目在构建构成中需要的依赖,比如我们去编译的时候依赖的第三方库等。可以向项目中现有的对象类型添加新的扩展属性、方法等,让你可以使用它们帮助我们配置、优化构建,比如android{}这个配置块就是Android Gradle插件为Project对象添加的一个扩展。可以对项目进行一些约定,比如应用java插件之后,约定src/main/java目录下是我们源代码存放的位置,在编译的时候也是编译这个目录下的java文件。
5.2 如何引用一个插件

应用二进制插件

什么是二进制插件?二进制插件就是实现了org.gradle.api.Plugin接口的插件,它们可以有plugin id。

apply plugin:'java'

这句代码就把java插件应用到我们项目中了,其中’java’是Java插件的plugin id,它是唯一的。对于Gradle自带的核心插件都有一个容易记的短名,称其为plugin id,比如这里的java,其实它对应的类型是org.gradle.api.plugins.JavaPlugin,所以通过该类型我们也可以应用这个插件:

apply plugin:org.gradle.api.plugins.JavaPlugin

又因为包org.gradle.api.plugins是默认导入的,所以我们可以去掉包名直接改为

apply plugin:JavaPlugin

以上三种写法是等价的,不过第一种用的最多,因为它容易记住,第二种写法一般适用于我们在build文件中自定义的插件,也就是脚本插件。

二进制插件一般被打包在一个jar里面独立发布,比如我们自定义的插件,在发布的时候我们也可以指定其plugin id,这个plugin id最好是一个全限定名称,就像包名一样,这样发布的插件plugin id就不会重复,比如org.flysnow.tools.plugin.xxx

应用脚本插件

version.gradle

ext {
versionName = '1.0.0'
versionCode = 1
}

build.gradle

apply from : 'version.gradle'

task ex52PrintTask {
println "app version is ${versionName}"
println "app version code is ${versionCode}"
}

其实这不能算是一个插件,只能算是一个脚本。应用脚本插件,其实就是把这个脚本加载进来,和二进制插件不同的是它用的是from关键字,后面紧跟着一个脚本文件,可以是本地的,也可以是网络存在的,如果是网络上的话要使用html url。

虽然它不是一个正真的插件,但是不能忽视它的作用,它是脚本文件模块化的基础,我们可以把庞大的脚本文件,进行分块,分段整理,拆分成一个个公用、职责分明的文件,然后使用apply from来引用它们,比如我们可以把常用的函数放在一个个utils.gradle文件里,供其他脚本文件引用。示例中我们把app各个版本名称和版本号单独放在一个脚本文件里,清晰、简单。我们也可以使用自动化对该文件自动处理,生成版本。

apply方法的其他用法

Project.apply()方法有3种方式,它们是以接受参数的不同区分的。我们上面用的是接受一个Map类型参数的方式。此外还有两种。

public abstract class ProjectDelegate public constructor() : org.gradle.api.Project {

	public open fun apply(closure: groovy.lang.Closure<*>): kotlin.Unit {  }

	public open fun apply(options: kotlin.collections.Map): kotlin.Unit {  }

	public open fun apply(action: org.gradle.api.Action): kotlin.Unit {  }
}

闭包的方式如下:

apply {
	plugin 'java'
}

该闭包被用来配置一个ObjectConfigurationAction对象,所以可以在闭包里使用ObjectConfigurationAction对象的方法、属性等进行配置。

ObjectConfigurationAction

public interface ObjectConfigurationAction {
    ObjectConfigurationAction to(Object... var1);

    ObjectConfigurationAction from(Object var1);

    ObjectConfigurationAction plugin(Class var1);

    ObjectConfigurationAction type(Class var1);

    ObjectConfigurationAction plugin(String var1);
}

plugin 'java’对应ObjectConfigurationAction plugin(Class var1);

Action的方式:

apply(new Action() {
    @Override
    void execute(ObjectConfigurationAction objectConfigurationAction) {
        objectConfigurationAction.plugin('java')
    }
})

对应public open fun apply(action: org.gradle.api.Action

5.3 自定义插件
apply plugin : ExCustomPlugin

class ExCustomPlugin implements Plugin {

 @Override
 void apply(Project project) {
     project.task('ex53CustomTask') {
         println "这是一个通过自定义插件创建的task"
     }
 }
}

wangzhiping@wangzhiping-PC:~/GradleProject$ gradle ex53CustomTask
Starting a Gradle Daemon (subsequent builds will be faster)

Configure project :
这是一个通过自定义插件创建的task

自定义插件必须要实现Plugin接口,这个接口只有一个apply方法,该方法在插件被应用时调用,所以我们可以实现这个方法,做我们想做的事情,比如这里创建一个名称为ex53CustomTask的任务。

这个插件定义在build脚本里,只能是自己的项目用,如果我们想开发一个独立的插件给所有人使用,应该怎么做呢?这就需要单独创建一个Groovy工程作为开发自定义插件的工程了。

groovy模块目录层级

── buildSrc
│   ├── build
│   │   ├── classes
│   │   │   └── groovy
│   │   │       └── main
│   │   │           └── com
│   │   │               └── henyiwu
│   │   │                   └── gradle
│   │   │                       ├── Ex53CustomPlugin$_apply_closure1$_closure2.class
│   │   │                       ├── Ex53CustomPlugin$_apply_closure1.class
│   │   │                       └── Ex53CustomPlugin.class
│   │   ├── generated
│   │   │   └── sources
│   │   │       └── annotationProcessor
│   │   │           └── groovy
│   │   │               └── main
│   │   ├── libs
│   │   │   └── buildSrc.jar
│   │   ├── resources
│   │   │   └── main
│   │   │       └── meta-INF
│   │   │           └── gradle-plugins
│   │   │               └── com.henyiwu.gradle.Ex53CustomPlugin.properties
│   │   ├── source-roots
│   │   │   └── buildSrc
│   │   │       └── source-roots.txt
│   │   └── tmp
│   │       ├── compileGroovy
│   │       │   └── groovy-java-stubs
│   │       └── jar
│   │           └── MANIFEST.MF
│   ├── build.gradle
│   └── src
│       └── main
│           ├── groovy
│           │   └── com
│           │       └── henyiwu
│           │           └── gradle
│           │               └── Ex53CustomPlugin.groovy
│           └── resources
│               └── meta-INF
│                   └── gradle-plugins
│                       └── com.henyiwu.gradle.Ex53CustomPlugin.properties
├── gradle
│   └── wrapper
│       ├── gradle-wrapper.jar
│       └── gradle-wrapper.properties
├── gradle.properties
├── gradlew
├── gradlew.bat
├── local.properties
└── settings.gradle

Ex53CustomPlugin.groovy

class Ex53CustomPlugin implements Plugin{

    @Override
    void apply(Project project) {
        project.task('ex53CustomTask') {
            println "这是一个通过自定义插件创建的task"
            doLast {
                println "ex53CustomTask do Last"
            }
        }
    }
}

resources/meta-INF/gradle-plugins/{pluginId}.properties

这里文件名对应com.henyiwu.gradle.Ex53CustomPlugin.properties

implementation-class=com.henyiwu.gradle.Ex53CustomPlugin

buildSrc/build.gradle

apply plugin: 'groovy'

dependencies {
    implementation gradleApi()
    implementation localGroovy()
}

运行结果

wangzhiping@wangzhiping-PC:~/AndroidStudioProjects/GradleTEst$ gradle ex53CustomTask

> Configure project :app
这是一个通过自定义插件创建的task

> Task :app:ex53CustomTask
ex53CustomTask do Last

BUILD SUCCESSFUL in 984ms
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/749821.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号