自定义Gradle插件相关知识

插件

Gradle内核本身提供的自动化构建功能十分有限,所有实际的功能都是通过插件的形势提供的,如编译Java代码的功能。通过插件可以:
1. 添加新的Tasks,比如JavaCompile Task
2. 在Gradle中添加新的对象,比如SourceSet对象,该对象用于添加一些约定的规则,像是Java源码放在src/main/java路径下
3. 扩展Gradle内核对象
4. 扩展其他插件的对象

一个工程需要首先apply Gradle插件,然后才能使用该插件。使用方法很简单,直接通过task名称执行这个task就行了。

可以直接在项目的构建脚本中添加逻辑,也可以通过插件的方式封装构建逻辑并应用。

相比于直接在项目的构建脚本中添加逻辑,通过apply插件的方式提供了更多的优点:

  1. 复用性强,可以把相似的逻辑通过插件的方式应用于多个工程
  2. 模块化的方式易于理解和管理
  3. 简化工程的构建脚本

下面来讲解通过插件的方式封装构建逻辑并使用的方式。

插件可以通过源码的方式使用,也可以打包成jar包使用。

一个Gradle插件打包了一系列的构建逻辑,可以应用在不同工程的构建中。可以使用不同的语言(Groovy,Java或Scala等)来编写一个Gradle插件,并编译成二进制文件使用。

插件代码可以放在不同的位置,如Build Script中,buildSrc文件夹下,或者放在一个单独的工程中。

  1. 放在Build Script中
    好处是插件代码会被自动编译并添加到classPath中,无需其他操作就能使用了。缺点是该插件只能在当前构建脚本中使用,无法在脚本之外使用

  2. 放在buildSrc文件夹(rootProjectDir/buildSrc/src/main/groovy)下
    优点是Gradle会自动编译并且添加到classPath中,当前工程里的构建脚本都可以使用该插件,缺点是插件不能被其他工程使用。

  3. 放在一个单独的工程中
    可以打包成一个jar包在任何工程里使用。该jar包可以包含一个或多个plugin,或者包含几个tasks,或者两者都有。

实现一个plugin很简单,只需要实现Plugin接口:

public interface Plugin<T> {
    void apply(T var1);
}

使用Gradle插件需要两步,第一步解析(resolve)插件,第二步应用(apply)插件到一个工程上。

解析插件是说找到指定版本的插件jar包并添加到构建脚本的classpath中,这个步骤完成后,插件的API就能在构建脚本中调用。只有通过jar包方式提供的插件才需要解析的过程,而通过脚本提供的插件自动完成解析。

应用插件就是在工程构建脚本中调用插件的apply方法,并传入当前project作为参数,这样插件可以通过传入的project参数修改project配置。解析和应用通过plugin DSL语法可以一步完成:

// 应用Gradle内核plugin可以使用短名称:
plugins { 
  id 'java'
}
// 应用社区plugin必须使用全名:
plugins {
  id "com.jfrog.bintray" version "0.4.1"
}

或者通过历史遗留方法分两步分别完成解析和应用:

buildscript {
    repositories {
        jcenter()
    }
    dependencies {
        classpath "com.jfrog.bintray.gradle:gradle-bintray-plugin:0.4.1"
    }
}

apply plugin: "com.jfrog.bintray"

构建脚本通过调用插件的apply方法来实例化一个插件对象,apply方法传入的参数就是当前的project对象。插件类可以通过这个project对象对project进行配置,比如添加一个task,下面的代码中插件在实例化时向project中添加了一个名为hello的task,该task会打印一句话:

apply plugin: GreetingPlugin

class GreetingPlugin implements Plugin<Project> {
    void apply(Project project) {
        project.task('hello') {
            doLast {
                println "Hello from the GreetingPlugin"
            }
        }
    }
}

通过gradle -q命令可以执行添加的hello task:

> gradle -q hello
Hello from the GreetingPlugin

需要注意的是,如果是在根目录的Build Script中apply了一个插件,那所有子项目中都会生成一个该插件的实例,可以控制在某些子项目中应用该插件,而另一些子项目中不应用。
settings.gradle

include 'helloA'
include 'helloB'
include 'goodbyeC'

build.gradle

plugins {
  id "org.gradle.sample.hello" version "1.0.0" apply false
  id "org.gradle.sample.goodbye" version "1.0.0" apply false
}

subprojects { subproject ->
  if (subproject.name.startsWith("hello")) {
    apply plugin: 'org.gradle.sample.hello'
  }
  if (subproject.name.startsWith("goodbye")) {
    apply plugin: 'org.gradle.sample.goodbye'
  }
}

处理生命周期
在经历生命周期的不同阶段时,构建脚本会接受到不同的通知消息(Notification),可以捕获这些消息并做一些相应的处理。
whenTaskAdded:当一个task被添加的时候

tasks.whenTaskAdded { task ->    
    switch (task.name) {        
        case 'bfd':            
            task.dependsOn 'assembleDebug'            
            break;        
        case 'bfr':            
            setAllConfig(app_version, online, version_name)            
            task.dependsOn 'assembleRelease'            
            break;    
    }
}

task bfd << {}

whenReady:配置完成后

gradle.taskGraph.whenReady { taskGraph -> 
    if (taskGraph.hasTask(release)) { 
        version = '1.0' 
    } else { 
        version = '1.0-SNAPSHOT' 
    }
}

beforeTask/afterTask:task执行前后

gradle.taskGraph.afterTask { Task task, TaskState state -> 
    if (state.failure) { 
        println "FAILED" 
    } else { 
        println "done" 
    }
}

更详细的流程可以看下面的代码演示:

gradle.afterProject {project, projectState ->
    println "afterProject $project"
}

allprojects {
    afterEvaluate { project ->
        println "afterEvaluate 1"
    }
}

gradle.taskGraph.whenReady { taskGraph ->
    println 'whenReady 2'
}

gradle.taskGraph.beforeTask { Task task ->
    if (task.name.equals("SayHello")) {
        println "beforeTask SayHello 3"
    }
}

task SayHello {
    println 'SayHello task  0'
    doFirst {
        println 'first 4'
    }

    doLast {
        println 'last 5'
    }
}

gradle.taskGraph.afterTask { Task task, TaskState state ->
    if (task.name.equals("SayHello")) {
        println "afterTask SayHello 6"
    }
}

输出如下:

SayHello task  0                                                                                                                                                                                                           
afterProject project ':app'                    
afterEvaluate 1             
afterProject project ':plugindemolib'
whenReady 2
beforeTask SayHello 3
first 4
last 5
afterTask SayHello 6

如何在插件中修改project中某个task的依赖,比如使check task依赖我们自定义的插件CodeCheckPlugin?可以通过下面的方式实现:

public class CodeCheckPlugin implements Plugin<Project> {

    void apply(Project project) {
        project.task('checkCodeAndSendEmail') << {
            println "Hello world"
        }

        project.tasks.whenTaskAdded{ theTask ->   
            if(theTask.name.equals("assembleDebug")){
                theTask.dependsOn "helloTask"
            }
        }
    }
}

这样在执行project的assembleDebug Task时,就会首先执行自定义的CodeCheckPlugin里面的checkCodeAndSendEmail,完成一些自定义的检查或逻辑。比如我们可以使checkCodeAndSendEmail依赖findBug Task,配合Gitlab和Jenkins,可以实现对提交代码的自动检查,并将检查结果发送邮件给相关开发人员。

参考:
http://blog.csdn.net/sbsujjbcy/article/details/50782830
https://docs.gradle.org/current/userguide/custom_plugins.html
http://blog.csdn.net/sbsujjbcy/article/details/50782830
https://neyoufan.github.io/2016/12/09/android/Jenkins-ci%E5%AE%B9%E5%99%A8%E5%8C%96%E5%9C%A8Android%E9%A1%B9%E7%9B%AE%E6%9E%84%E5%BB%BA%E4%B8%AD%E7%9A%84%E5%BA%94%E7%94%A8%EF%BC%88%E5%85%AC%E4%BC%97%E5%8F%B7%E7%89%88%EF%BC%89/

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

推荐阅读更多精彩内容