自定義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文件夾下,或者放在一個單獨的工程中。
-
放在Build Script中
好處是插件代碼會被自動編譯並添加到classPath中,無需其他操作就能使用了。缺點是該插件只能在當前構建腳本中使用,無法在腳本之外使用 -
放在buildSrc文件夾(rootProjectDir/buildSrc/src/main/groovy)下
優點是Gradle會自動編譯並且添加到classPath中,當前工程裏的構建腳本都可以使用該插件,缺點是插件不能被其他工程使用。 -
放在一個單獨的工程中
可以打包成一個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項目構建中的應用(公衆號版)/