自定義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項目構建中的應用(公衆號版)/

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