自定义Gradle插件相关知识

自定义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容器化在Android项目构建中的应用(公众号版)/

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章