Gradle任务

1.多种方式创建任务:

在Gradle中,我们可以有很多种方式来创建任务。这都依赖于Project给我们提供的快捷方法以及TaskContainer提供的相关onCreate方法。

第一种:直接以一个任务名字创建任务的方式

def Task ex6=task(ex6)

ex6.doLast{

println "创建方法原型为:Task task(String name)"

}

我们可以使用gradle ex6来执行这个任务,这种方式的创建其实是调用Project对象中的task(String name)的方法。

第二种:是以一个人物名字+一个队该任务配置的Map对象来创建

def Task ex7=task(ex7,group:BasePlugin.BUILD_GROUP)

ex7.doLast{

println "创建方法原型为:Task task(Map<String,?> args,String name)"

println "任务分组:${ex7.group}"

}

和第一种方法大同小异,只是多了一个Map参数,用于对要创建的Task进行配置。比如我们例子里为其指定了分组为BUILD,我们通过执行该任务可以看到我们的配置起了作用。Map中可用的配置如下:

image.png

第三种:任务名字+闭包配置的方式

task ex8{

description '演示创建'

doFirst{

    println "创建方法原型:Task task(String name,Closure configureClosure)"

    println "任务描述:${description}"

}

}

因为Map参数配置的方式(第二种)可配置项有限,所以可以通过闭包的方式进行更多灵活的配置。闭包里的委托对象就是Task对象的任何方法、属性等信息进行配置,比如示例中我们配置了任务的描述和任务执行后要做的事情。

下面我们说一下TaskContainer创建任务的方式。如果我们去查看Project对象中关于上面我们演示的Task方法的源代码,就会发现它们最终都是调用TaskContainer对象中的create方法,其参数和Project中的Task方法基本一样。我们使用这种方式重写第三种方式的例子:

tasks.create('ex9'){

description '演示'

doLast{

    println "创建方法原型为:Task create(String name,Closure con)"

    println "任务描述:${description}"

}

}

tasks是Project对象的属性,其类型是TaskContainer,我们可以使用它来直接创建任务。

2.多种方式访问任务

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

task ex10

ex10.doLast{

println 'ex10.doLast'

}

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

task ex11

tasks['ex11'].doLast{

println 'ex11.doLast'

}

访问的时候,任务名就是Key(关键索引)。其实这里说Key不恰当,因为tasks并不是一个Map。[]在Groovy中是一个操作符,我们知道Groovy的操作符都有对应的方法让我们重载,a[b]对应的是a.getAt(b)这个方法,对应的例子tasks['ex11’]其实就是调用tasks.getAt('ex11')这个方法。如果我们查看Gradle源码的话,最后发现是调用findByName(String name)实现的。

通过路径或者名称访问,有两种方式,一种是get,另一种是find,它们的区别在于get的时候如果找不到该任务就会抛出UnknownTaskException异常,而find在找不到该任务的时候会返回null:

task ex12

tasks['ex12'].doLast{

println tasks.findByPath('ex12')

println tasks.getByName('ex12')

println tasks.findByName('ddd')

}

3.任务分组和描述

任务是可以分组和添加描述的,任务的分组其实就是对任务的分类,便于我们对任务进行整理,这样清晰明了。

def Task myTask=task ex13

myTask.group=BasePlugin.BUILD_GROUP

myTask.description='这是构建引导'

myTask.doLast{

println "group:${group},des:${description}"

}

执行gradle tasks查看任务信息的时候,就能看到该任务已经被归类到Build tasks分类里,并且可以看到该任务的描述信息:

Build tasks


assemble - Assembles the outputs of this project.

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.

classes - Assembles main classes.

clean - Deletes the build directory.

ex13 - 这是构建引导

ex7

jar - Assembles a jar archive containing the main classes.

testClasses - Assembles test classes.

上面输出可以看到,我们刚刚新建的任务被归类到build分组下了,这样我们可以很方便地找到该任务。

4.<<操作符

"<<"操作符在Gradle的Task上是doLast方法的短标记形式,也即是说“<<”可以代替doLast:

task ex14{

    println 'ex14'

}

ex14<<{

    println "ex14<<"

}

ex14.doLast{

    println "ex14DoLast"

}

"<<"操作符在Groovy中是可以重载的,a<<b对应的是a.leftShift(b)方法,所以Task接口中肯定有一个leftShift方法重载了“<<”操作符,查看Task源码:

/**

  • <p>Adds the given closure to the end of this task's action list. The closure is passed this task as a parameter

  • when executed. You can call this method from your build script using the << left shift operator.</p>

  • @param action The action closure to execute.

  • @return This task.

  • @deprecated Use {@link #doLast(Closure action)}

*/

@Deprecated

Task leftShift(Closure action);

从注释中我们可以看到,说明了可以使用“<<”操作符。doLast和leftShift有没有区别呢?我们看一下两个方法的实现:

@Override

public Task doLast(final Closure action) {

hasCustomActions = true;

if (action == null) {

    throw new InvalidUserDataException("Action must not be null!");

}

taskMutator.mutate("Task.doLast(Closure)", new Runnable() {

    public void run() {

        getTaskActions().add(convertClosureToAction(action));

    }

});

return this;

}

@Override

public Task leftShift(final Closure action) {

DeprecationLogger.nagUserWith("The Task.leftShift(Closure) method has been deprecated and is scheduled to be removed in Gradle 5.0\. Please use Task.doLast(Action) instead.");

hasCustomActions = true;

if (action == null) {

    throw new InvalidUserDataException("Action must not be null!");

}

taskMutator.mutate("Task.leftShift(Closure)", new Runnable() {

    public void run() {

        getTaskActions().add(taskMutator.leftShift(convertClosureToAction(action)));

    }

});

return this;

}

上面程序中关键的依据代码是actions.add(),这一句就是我们配置的操作转换为Action放在actions这个List里面,是直接放在末尾,所以它们两个效果是一样的。但是,从leftShit方法中可以看出,leftShift不能直接被调用了,否则会报异常信息。

5.任务的执行分析

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

@Override

public List<Action<? super Task>> getActions() {

if (observableActionList == null) {

    observableActionList = new ObservableActionWrapperList(getTaskActions());

    observableActionList.addPropertyChangeListener(new PropertyChangeListener() {

        public void propertyChange(PropertyChangeEvent evt) {

            taskMutator.assertMutable("Task.getActions()", evt);

        }

    });

}

return observableActionList;

}

@Override

public List<ContextAwareTaskAction> getTaskActions() {

if (actions == null) {

    actions = new ArrayList<ContextAwareTaskAction>();

}

return actions;

}

现在我们把Task之前执行、Task本身执行以及Task之后执行分别称为doFirst、doSelf和doLast,下面以一个列子来演示:

def Task task15=task ex15(type: CustomTask)

task15.doFirst{

println 'Task执行之前doFirst'

}

task15.doLast{println 'Task执行之后doLast'}

class CustomTask extends DefaultTask{

@TaskAction

def doSelf(){

    println 'Task自己在执行doSelft'

}

}

输出:

Task :ex15

Task执行之前doFirst

Task自己在执行doSelft

Task执行之后doLast

例子中我们定义了一种Task类型CustomTask,并声明了一个方法doSelf,该方法被TaskAction注解标注,意思是该方法就是Task本身要执行的方法。

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

而foFirst和doLast分别是往actions List的第一个位置和最后一个位置分别添加一个action。所以最后输出的就是doFirst、doSelft、doLast的顺序。

5.“任务排序”

其实并没有真正的任务排序功能,这个排序不像我们想象的通过设置优先级或者Order顺序实现,而是通过任务的shouldRunAfter和mustRunAfter这两个方法,它们可以控制一个任务应该或者一定在某个任务之后执行。通过这种方式你可以在某些情况下控制任务的执行顺序,而不是通过强依赖的方式。

这个功能是非常有用的,比如我们公司自己设置的顺序是,必须先执行单元测试,然后才能进行打包,这就保证了App的质量。

taskB.shouldRunAfter(taskA)表示taskB应该在taskA执行之后执行,这里的应该而不是必须。所以有可能任务顺序并不会按预设的执行。

taskB.mustRunAfter(taskA)表示taskB必须在taskA执行之后执行,这个规则就比较严格了。

task ex16<<{

println 'ex16'

}

task ex17<<{

println 'ex17'

}

gradle ex16 ex17输出:

Task :ex16

ex16

Task :ex17

ex17

当加上这句代码:ex16.mustRunAfter ex17

在执行:gradle ex16 ex17

输出:

Task :ex17

ex17

Task :ex16

ex16

6.任务的禁用和启用

Task中有个enabled属性,用于启用和禁用任务,默认是true,表示启用;设置为false,则禁止该任务执行。

task ex18<<{

println "ex18"

}

ex18.enabled=false

在某些情况下,可以通过该属性灵活控制任务的执行,这种方式需要在执行到具体逻辑的时候才能进行判断设置。

7.任务的onlyIf断言

断言就是一个条件表达式。Task有一个onlyIf方法,它接受一个闭包作为参数,如果该闭包返回true则该任务执行,否则跳过。这个有很多用途,比如控制程序哪些情况下打什么包,什么时候执行单元测试,什么情况下执行单元测试的时候不执行网络测试等。

加入我们的收发渠道是应用宝,执行执行build会编译出来所有包,这个太慢了也不符合需求,现在我们就采用onlyIf的方式通过属性来控制:

final String ALL="all"

final String SHOUFA="shoufa"

final String EXC_SHOUFA="exclude_shoufa"

task exQQ<<{

println "应用宝包"

}

task exBaidu<<{

println "百度包"

}

task build{

group BasePlugin.BUILD_GROUP

description "打渠道包"

}

build.dependsOn exQQ,exBaidu

exQQ.onlyIf{

def execute=false;

if(project.hasProperty("build_apps")){

    Object buildApps=project.property("build_apps")

    if(SHOUFA.equals(buildApps)||ALL.equals(buildApps)){

        execute=true

    }else{

        execute=false

    }

}else{

    execute=true

}

execute

}

exBaidu.onlyIf{

def execute=false;

if(project.hasProperty("build_apps")){

    Object buildApps=project.property("build_apps")

    if(EXC_SHOUFA.equals(buildApps)||ALL.equals(buildApps)){

        execute=true

    }else{

        execute=false

    }

}else{

    execute=true

}

execute

}

打包所有渠道包

gradle -Pbuild_apps=all build

打首发包

gradle -Pbuild_apps=shoufa build

打非首发包

gradle -Pbuild_apps=exclude_shoufa build

通过上面3个命令就可以动态控制我们要打那些渠道包。

命令行中-P的意思是为Project指定K-V格式的属性键值对,使用格式为-PK=V。

8.任务规则

我们想获取一个TaskContainer中名为taskObject的元素,但这个任务可能不存在,这个时候我就就需要添加规则来处理这种异常情况了:

tasks.addRule("测试的一个用例"){String taskName->

task(taskName)<<{

    println "该${taskName}任务不存在"

}

}

执行:gradle aa

输出:

Task :dd

该dd任务不存在

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,378评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,356评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,702评论 0 342
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,259评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,263评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,036评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,349评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,979评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,469评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,938评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,059评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,703评论 4 323
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,257评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,262评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,485评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,501评论 2 354
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,792评论 2 345

推荐阅读更多精彩内容