目前為止,我們已經學習了如何修改Gradle構建屬性,以及如何運行任務。本章,我們會深入了解這些屬性,并且創建我們自己的任務。一旦我們學會了創建任務,就可以更進一步,學習如何創建Gradle插件。
在學習創建任務之前,我們需要學習一些重要的Groovy概念。因為了解一些Groovy基礎有助于我們自定義任務和插件。學習Groovy也可以幫助我們了解Gradle的原理,以及構建文件的書寫格式。
本章內容有:
- 了解Groovy
- 學習任務
- 接入Android插件
- 創建插件
了解Groovy
由于多數Android開發者精通Java,所以將Groovy和Java對比學習相對要容易一些。Groovy對于Java開發者來說很容易閱讀,但如果不簡單了解一下的話,編寫起來還是有一定難度的。
使用Groovy Console來學習Groovy是一個很好的途徑。這個應用包含在Groovy SDK中,可以立即執行Groovy代碼得到結果。Groovy Console也可以運行純Java代碼,可以更加方便的對比Java和Groovy代碼。你可以從http://groovy-lang.org/download.html下載帶有Groovy Console的Groovy SDK。
簡介
Groovy源自Java,并在JVM上運行。Groovy是一種簡單、直接的語言,既是腳本,也是一種成熟的編程語言。本節我們通過對比來了解Groovy的原理以及和Java的不同。
Java打印字符串代碼如下:
System.out.println("Hello, world!");
而Groovy如下:
println 'Hello, world!'
兩者有幾個關鍵的不同之處:
- 沒有
System.out
命名空間 - 方法的參數沒有加圓括號
- 語句末尾沒有分號
示例同樣在字符串的兩側使用了單引號。你也可以使用雙引號,但兩者作用不完全一樣。雙引號的字符串可以包含插值表達式。插值是計算包含占位符的字符串的過程,并將占位符替換為真實值。占位符表達式可以是變量或者方法。包含方法或者多個變量的占位符表達式需要放入{}
中,并以$
作為前綴;僅包含單個變量的占位符表達式只需要以$
為前綴。下面是一些例子:
def name = 'Andy'
def greeting = "Hello, $name!"
def name_size "Your name is ${name.size()} characters long."
greeting
變量的值是"Hello, Andy",name_size
的值是"Your name is 4 characters long."。
字符串插值也可以允許你動態執行代碼,如下:
def method = 'toString'
new Date()."$method"()
如果你已經習慣了java,這看起來會很陌生,但這是動態編程語言的正常的語法和行為。
類和成員
Groovy創建類和Java很相似。下面是包含一個成員的類:
class MyGroovyClass {
String greeting
String getGreeting() {
return 'Hello!'
}
}
注意類和成員都沒有明確的訪問修飾符。Groovy默認的訪問修飾符和Java不一樣。類和方法默認是public的,成員默認是private的。
下面創建MyGroovyClass
的實例:
def instance = new MyGroovyClass()
instance.setGreeting 'Hello, Groovy!'
instance.getGreeting()
你可以使用def
關鍵字來創建新的變量。在有了類的示例之后,你就可以操作類成員了。Groovy會自動添加成員的訪問器。你也可以覆寫它們,比如這里我們覆寫了getGreeting()
方法。
你也可以直接調用成員,這實際也是在調用getter方法。也就是說你可以輸入instance.greeting
來代替instance.getGreeting()
:
println instance.getGreeting()
println instance.greeting
兩種方式結果一樣。
方法
和變量相似,你不需要為方法指定返回類型。你可以這么做,哪怕只是為了簡潔。Groovy和Java方法的另一個不同之處是最后一行默認是返回值,即使沒有使用return
關鍵字。
為了展示Java和Groovy方法的不同,參照下面的Java方法:
public int square(int num) {
return num * num;
}
square(2);
你需要指定方法的訪問類型、返回值類型、參數類型,并在最有一行使用return
關鍵字。
在Groovy中,這個方法如下:
def square(def num) {
num * num
}
square 4
返回值類型和參數類型不必指明。def
關鍵字取代了明確的類型,返回值不需要return
關鍵字。盡管如此,為了清晰,還是推薦使用return
關鍵字。調用方法時,你不必為它添加圓括號。
下面是Groovy更簡潔的定義方法的形式:
def square = { num ->
num * num
}
square 8
這不是一個常規的方法,而是一個閉包。Java沒有類似閉包的概念。閉包在Groovy和Gradle中有非常重要的作用。
Closures(閉包)
閉包是一個可以有輸入和輸出的匿名代碼塊。它可以用來給變量賦值,也可以作為方法的參數。
你可以在花括號中添加代碼塊來定義簡單的閉包。如果你想更明確一點,你可以將類型添加到定義中:
Closure square = {
it * it
}
square 16
Closure
顯示指明了代碼塊的類型為閉包。前面的示例還介紹了隱式無類型參數it
的概念。如果你沒有為閉包顯示添加參數,Groovy會自動添加一個,參數名為it
,你可以在所有閉包中使用它。如果調用者沒有指定任何參數,it
的值為null。這使代碼稍微簡潔一些,但只在有單個參數時有效。
在Gradle中,我們每時每刻都在使用閉包。本書中,目前為止我們說的塊就是指閉包。也就是說,android
塊和dependencies
塊都是閉包。
集合
在Gradle中使用Groovy時,有兩個集合類型:list和map。
Groovy創建list非常簡單。只需要:
List list = [1, 2, 3, 4, 5]
list的遍歷也非常簡單。可以使用each
方法:
list.each() { element ->
println element
}
你也可以使用it
來精簡代碼:
list.each() {
println it
}
map在Gradle的一些配置和方法中使用。map是一個鍵值對的集合,可以如下定義:
Map pizzaPrices = [margherita:10, pepperoni:12]
可以使用get
方法或者方括號來訪問map。
pizzaPrices.get('pepperoni')
pizzaPrices['pepperoni']
Groovy還有一個更加簡單的方式。可以直接使用map.key
的語法來得到相應的值:
pizzaPrices.pepperoni
Groovy in Gradle
現在我們已經了解了Groovy的基礎,是時候回過頭來重新看下Gradle的構建文件了。我們可以比較容易地理解配置語法。比如,Android插件是如何被引入構建的:
apply plugin: 'com.android.application'
這塊代碼使用了Groovy的簡寫。完全不簡寫的話,它是這個樣子的:
project.apply([plugin: 'com.android.application'])
這樣理解起來局容易多了。apply
是Project
類的一個方法,接收一個map
參數,其中鍵為plugin
,值為com.android.application
。
另一個例子是dependencies
塊:
dependencies {
compile 'com.google.code.gson:gson:2.3'
}
現在我們知道了這個塊是一個閉包,作為Project
類dependencies()
方法的參數。這個閉包會傳遞給DependencyHandler
對象,該對象有add()
方法。這個方法由三個參數:一個定義配置的字符串,一個定義依賴符號的對象,一個包含這個依賴的屬性的閉包。它的全寫如下:
project.dependencies({
add('compile', 'com.google.code.gson:gson:2.3', {
// Configuration statements
})
})
到現在為止你應該已經可以看懂這些配置文件了。
如果你想學習更多Gradle是如何使用Groovy的,你可以從看Project的官方文檔看起,地址為: http://gradle.org/docs/current/javadoc/org/gradle/api/Project.html
學習任務
自定義Gradle任務可以顯著提高開發者的效率。任務可以操作已有的構建過程,添加新的構建階段,或者影響構建的輸出。你可以執行簡單的任務,比如重命名生成的APK。任務可以使你運行更為復雜的代碼,比如在app打包前,生成幾個不同密度的圖像。在學會創建任務之后,你就有能力去改變構建過程的方方面面。當你學習如何使用Android插件時,這一點尤其正確。
定義任務
任務屬于一個Project
對象,每個任務都實現了Task
接口。最簡單的定義任務的方法是執行task
方法,并將任務名稱傳遞給它:
task hello
這就創建了一個任務,但是執行它不會做任何事情。想要創建一個稍微有點作用的任務,你需要為它添加動作。初學者常犯的一個錯誤是這樣創建任務:
task hello {
println 'Hello, world!'
}
執行的結果如下:
$ gradlew hello
Hello, world!
:hello
從結果看,你可能認為這個任務做事情了,但實際上,"Hello,world!"是在任務執行之前打印出來的。為了理解這個地方發生了什么,我們需要回過頭來看下基礎的東西。在第一章,我們學習了Gradle構建的生命周期:初始化階段、配置階段和執行階段。在你像上例中那樣添加任務時,你實際上設置了任務的配置。即使你執行其他的任務,"Hello,world!"也會打印。
如果你想在執行階段為任務添加動作,使用如下方式:
task hello << {
println 'Hello, world!'
}
唯一的區別是閉包前的<<
符號。這會告知Gradle這段代碼是為執行階段準備的,而不是配置階段。
為了展示它們的區別,看下面的構建文件:
task hello << {
println 'Execution'
}
hello {
println 'Configuration'
}
我們定義了任務hello
,在執行時會打印"Execution"。我們也為hello
任務的配置階段定義了代碼,即打印"Configuration"。即使配置塊是在真正的任務定義代碼之后定義的,它也會先被執行。上例的輸出為:
$ gradlew hello
Configuration
:hello
Execution
錯誤的使用配置階段導致任務失敗是一個很常見的錯誤。在創建任務時需要牢記。
由于Groovy有很多簡寫,Gradle定義任務有如下幾種形式:
task(hello) << {
println 'Hello, world!'
}
task('hello') << {
println 'Hello, world!'
}
tasks.create(name: 'hello') << {
println 'Hello, world!'
}
前兩個塊是Groovy實現相同功能的兩種不同方式。你可以使用圓括號,但不是必須的。你也可以不用單引號。在這兩個塊中,我們調用了task()
方法,它需要兩個參數:一個任務名稱和一個閉包。task()
方法是Project
類的方法。
最有一個塊沒有使用task()
方法,而是使用了Project
類的tasks
對象,該對象是TaskContainer
的實例。這個類提供了一個create()
方法,接收一個Map
和一個閉包作為參數,返回一個Task
。
使用簡寫是非常便捷的書寫方式,很多在線的示例和教程都是使用的簡寫。而全寫對初次學習非常有用,這樣,Gradle才看起來不那么神奇,理解起來更加容易。
任務剖析
Task
接口是所有任務的基礎,定義了屬性和方法的集合。所有這些被DefaultTask
類實現。這是一個標準的任務類型的實現,在你定義一個新的任務時,它是基于DefaultTask
的。
從技術上講,
DefaultTask
并不是真正的Task
接口所有方法的實現類。Gradle有一個名為AbstractTask
的內部類,包含所有方法的實現。因為AbstractTask
是內部類,我們不能繼承它。所以我們才關注繼承自AbstractTask
的DefaultTask
類,并覆寫它。
每個任務有一個Action
對象的集合。在執行任務時,所有的動作會被順序執行。你可以使用doFirst()
和doLast()
方法添加動作。這兩個方法都以閉包作為參數,然后將其封裝成一個Action
對象。
你只有使用doFirst()
或者doLast()
方法才能為任務添加在執行階段運行的代碼。我們之前使用的左移操作符<<
是doFirst()
方法的簡寫。
下面是使用這兩個方法的例子:
task hello {
println 'Configuration'
doLast {
println 'Goodbye'
}
doFirst {
println 'Hello'
}
}
執行hello任務,輸出如下:
$ gradlew hello
Configuration
:hello
Hello
Goodbye
你也可以多次調用doFirst()
和doLast()
:
task mindTheOrder {
doFirst {
println 'Not really first.'
}
doFirst {
println 'First!'
}
doLast {
println 'Not really last.'
}
doLast {
println 'Last!'
}
}
該任務的輸出為:
$ gradlew mindTheOrder
:mindTheOrder
First!
Not really first.
Not really last.
Last!
注意doFirst()
總是將動作加到任務的開頭,doLast()
總是加到末尾。你在使用這樣的方法時需要謹慎,尤其是對順序有要求時。
對于需要順序執行的任務,你可以使用mustRunAfter()
方法。這個方法允許你改變Gradle構建的依賴圖。
task task1 << {
println 'task1'
}
task task2 << {
println 'task2'
}
task2.mustRunAfter task1
同時執行兩個任務,task1總會在task2之前運行:
$ gradlew task2 task1
:task1
task1
:task2
task2
mustRunAfter()
方法并沒有在任務間添加依賴,你仍然可以單獨執行task2,而不必執行task1。如果你需要讓一個任務依賴另一個任務,可以使用dependsOn()
方法。mustRunAfter()
和dependsOn()
的區別可以從下面的示例提現:
task task1 << {
println 'task1'
}
task task2 << {
println 'task2'
}
task2.dependsOn task1
單獨執行task2,得到的結果如下:
$ gradlew task2
:task1
task1
:task2
task2
使用mustRunAfter()
,在同時執行task1、task2時,task1總是在task2之前執行,但是它們仍然可以單獨執行。而使用dependsOn()
,單獨執行task2總會先觸發執行task1。這是一個明顯的不同之處。
使用任務來簡化發布過程
在你將應用發布到Google Play商店之前,你需要用證書進行簽名。你需要有自己的keystore,它包含一組私鑰。在你有了keystore和私鑰之后,可以在配置文件中定義簽名配置:
android {
signingConfigs {
release {
storeFile file("release.keystore")
storePassword "password"
keyAlias "ReleaseKey"
keyPassword "password"
}
}
buildTypes {
release {
signingConfig signingConfigs.release
}
}
}
這種方法的缺點是,你的keystore的密碼以純文本的方式保存在倉庫中。如果你是在做開源項目,這是絕對不行的;任何一個有keystore文件和私鑰的人可以用你的身份發布應用。為了防止這種情況的發生,你需要創建一個任務,在每次生成發布包時,查詢發布密碼。雖然這有點麻煩,但它確實能使構建服務器自動生成發布版本。一個不錯的保存keystore密碼的解決方案是創建一個不保存在倉庫中的配置文件。
在項目的根目錄創建一個private.properties
文件,添加如下代碼:
release.password = thepassword
我們假設keystore和key本身的密碼相同。如果你的密碼不同,也可以添加第二個屬性。
設置好這些,你可以定義一個getReleasePassword
的任務:
task getReleasePassword << {
def password = ''
if (rootProject.file('private.properties').exists()) {
Properties properties = new Properties();
properties.load( rootProject.file('private.properties').newDataInputStream())
password = properties.getProperty('release.password')
}
}
這個任務會在項目根目錄查詢一個名額外private.properties
的文件。如果文件存在,該任務會加載所有的屬性信息。properties.load()
方法會查詢鍵值對,比如我們定義的release.password
。
為了確保任何人在沒有private.properties
文件或者屬性文件存在,但沒有release.password
屬性的情況下都可以運行腳本,需要添加一個反饋。如果密碼為空,在控制臺要求輸入密碼:
if (!password?.trim()) {
password = new String(System.console().readPassword("\nWhat's the secret password? "))
}
Groovy檢查字符串是否為空是非常簡單的。password?.trim()
中的問號是一個null檢查,在password為null時,不會調用trim()
方法。我們不用進行明確的空判斷,因為空的字符串在if
語句中會返回false
。
new String()
是必須的,因為System.readPassword()
會返回一個字節數組,我們需要顯示轉換為字符串。
在有了keystore的密碼之后,我們就可以為release版本配置簽名配置了。
android.signingConfigs.release.storePassword = password
android.signingConfigs.release.keyPassword = password
任務寫完了,我們還要確保被執行。在build.gradle
文件中添加如下代碼:
tasks.whenTaskAdded { theTask ->
if (theTask.name.equals("packageRelease")) {
theTask.dependsOn "getReleasePassword"
}
}
這段代碼會將一個閉包與Gradle和Android插件掛鉤,在任務檢入依賴圖時,代碼會被執行。packageRelease
執行之前,不會要求密碼,所以我們確保packageRelease
依賴getReleasePassword
任務。我們不可以僅僅使用packageRelease.dependsOn()
,因為打包任務是Android插件動態生成的,依賴于構建變體。也就是說,在Android插件查詢到所有的構建變體之前,packageRelease
任務是不存在的。這個查找過程在構建之前開始。
想要這個任務工作,跟Gradle和Android插件掛鉤是必須的。這是一個強有力的概念,我們會探索更多的細節。
與Android插件掛鉤
在Android開發中,我們想要影響的多數任務都和Android插件相關。可以通過掛鉤構建過程來增加任務的行為。上例我們學習到了在常規構建中,怎樣向一個Android任務添加新的依賴任務。本節,我們將研究一些Android特有構建的鉤子。
一個掛鉤Android插件的方法是修改構建變體。這么做非常直接,你只需要用下面的代碼片段就可以遍歷所有的構建變體:
android.applicationVariants.all { variant ->
// Do something
}
你可以使用applicationVariants
對象來獲取構建變體的集合。拿到構建變體的引用后,你就可以訪問和更改它的屬性了,比如name,description等。如果你想在Android library上使用相同的邏輯,可以使用libraryVariants
對象。
注意我們遍歷構建變體時,使用的是
all()
而不是each()
方法。因為each()
的觸發是在evaluation階段,在Android插件創建構建變體之前。all()
方法在每次集合加入新元素時就會觸發。
這個鉤子可以用來在APK保存之前修改名稱,或者在文件名中添加版本號。這就使維護APK變得容易,因為你不需要手動編輯文件名。下一節我們會學習如何實現它。
自動重命名APK
一個常見的修改構建構成的應用場景是在APK打包后,重命名以包含版本信息。你可以遍歷應用的構建變體,修改outputFile
屬性來達到目的:
android.applicationVariants.all { variant ->
variant.outputs.each { output ->
def file = output.outputFile
output.outputFile = new File(file.parent,file.name.replace(".apk", "-${variant.versionName}.apk"))
}
}
每個構建變體有一個輸出的集合。一個Android應用的輸出是一個APK。每個輸出對象有一個File
類型的屬性outputFile
。知道了輸出的路徑之后,你就可以修改它。本例中我們在文件名中加入了版本信息。
強大的Android插件鉤子和簡潔的Gradle任務的結合給了我們無限的可能。下一節,我們學習如何為應用的每個構建變體創建一個任務。
動態創建任務
基于Gradle的工作方式和任務的構造方式,在Android構建變體的基礎上,我們可以在配置階段創建新的任務。為了展示這個強大的概念,我們將創建一個任務,用來安裝、運行Android應用的任何一個構建變體。install
任務是Android插件定義的,但是如果你是用命令行運行installDebug
任務來安裝應用,安裝結束后,你需要手動啟動它。本節我們將創建的任務會解決最后一步的問題。
首先與applicationVariants
屬性掛鉤:
android.applicationVariants.all { variant ->
if (variant.install) {
tasks.create(name: "run${variant.name.capitalize()}", dependsOn: variant.install) {
description "Installs the ${variant.description} and runs the main launcher activity."
}
}
}
對每一個變體,檢查它是否有install
任務。這是必需的,因為我們要創建的新的任務需要依賴于它。確定install
任務存在后,我們基于變體的名稱創建一個新任務,該任務依賴于variant.install
,執行時會觸發install
任務。task.create()
的閉包中添加了描述字段,在執行gradlew tasks
時會顯示出來。
除了添加描述外,我們還需要添加任務執行的動作。本例中,我們想要啟動應用。Android Debug Tool(ADB)提供了啟動真機或者模擬器應用的命令:
$ adb shell am start -n com.package.name/com.package.name.Activity
Gradle有一個exec()
的方法,可以執行命令行。為使exec()
工作,我們需要加到PATH環境變量中。我們同樣需要把所有的參數傳遞給args
屬性:
doFirst {
exec {
executable = 'adb'
args = ['shell', 'am', 'start', '-n',"${variant.applicationId}/.MainActivity"]
}
}
為了獲取包名,可以使用構建變體的applicationId,它可能包含一個后綴(如果提供的話)。在這個例子中,后綴會有一個問題。即使我們添加了后綴,activity的路徑也是不會變的,比如:
android {
defaultConfig {
applicationId 'com.gradleforandroid'
}
buildTypes {
debug {
applicationIdSuffix '.debug'
}
}
}
包名是com.gradleforandroid.debug
,但是activity的路徑依然是com.gradleforandroid.Activity
。為了得到正確的路徑,我們需要去掉后綴:
doFirst {
def classpath = variant.applicationId
if(variant.buildType.applicationIdSuffix) {
classpath -= "${variant.buildType.applicationIdSuffix}"
}
def launchClass = "${variant.applicationId}/${classpath}.MainActivity"
exec {
executable = 'adb'
args = ['shell', 'am', 'start', '-n', launchClass]
}
}
首先,我們創建了一個classpath
變量,并賦值為applicationId。接下來,查找后綴。在Groovy中,字符串可以使用-
做減法。這些更改確保在添加后綴的情況下,應用安裝完成后可以自動運行。
創建插件
如果你有一些想要在多個工程中復用的任務,可以考慮將任務提取到自定義的插件中。這樣你就可以復用構建邏輯,或者將它分享出去。
插件可以由Groovy編寫,也可以基于JVM的其它語言,比如Java、Scala。實際上,Gradle Android插件的大部分是有Java和Groovy混合編寫的。
創建一個簡單的插件
為了提取構建文件中的構建邏輯,你可以在build.gradle
文件中創建一個插件。這是最簡單的自定義插件的方式。
為了創建插件,首先需要創建一個實現了Plugin
接口的類。我們將使用本章已寫過的動態創建run
任務的代碼。插件類如下:
class RunPlugin implements Plugin<Project> {
void apply(Project project) {
project.android.applicationVariants.all { variant ->
if (variant.install) {
project.tasks.create(name:"run${variant.name.capitalize()}", dependsOn: variant.install) {
// Task definition
}
}
}
}
}
Plugin
接口定義了apply()
方法。構建文件使用插件時,Gradle會調用這個方法。project
會作為參數傳遞過來,這樣插件就可以配置項目或者使用項目的方法和屬性。在上一個例子中,我們不能從Android插件直接調用屬性,而是應該首先訪問project對象。需要注意的是,這要求Android插件在我們的插件之前被引入到項目中,否則,project.android
會引發異常。
任務的代碼和之前是一樣的,只是exec()
需要用project.exec()
替換。
將該插件引入到構建文件中:
apply plugin: RunPlugin
分發插件
為了分發插件并分享給其他人,你需要將它移動到一個單獨的模塊(或項目)中。單獨的插件有它自己的構建文件來配置依賴和分發的方法。這個模塊生成一個JAR文件,包含插件類和屬性。你可以使用這個JAR文件將插件應用到模塊或者項目中,也可以分享給他人。
和其他Gradle項目一樣,創建一個build.gradle
來配置構建:
apply plugin: 'groovy'
dependencies {
compile gradleApi()
compile localGroovy()
}
由于我們是用Groovy寫的插件,所以需要引入Groovy插件。Groovy插件繼承自Java插件,使我們可以構建和打包Groovy類。Groovy同樣支持Java,你可以混合使用。你甚至可以使用Groovy擴展Java類。這使Groovy很容易上手。
gradleApi()
依賴用于在我們的插件中訪問Gradle的命名空間。
localGroovy()
依賴是Groovy SDK在Gradle中安裝的發行版。
這兩個依賴是Gradle自動添加的。
如果你計劃公開發布你的插件,請確保在構建文件中定義了group和version信息,例如
group = 'com.gradleforandroid'
version = '1.0'
為了使用獨立模塊的代碼,我們還需要確保正確的目錄結構:
plugin
└── src
└── main
├── groovy
│ └── com
│ └── package
│ └── name
└── resources
└── META-INF
└── gradle-plugins
和其他的Gradle模塊一樣,我們需要提供一個src/main
目錄。因為這是一個Groovy工程,所以main的子目錄是groovy
而不是java
。main的另一個子目錄是resources
,用來指定插件的屬性。
我們在包目錄創建了一個RunPlugin.groovy
的類文件:
package com.gradleforandroid
import org.gradle.api.Project
import org.gradle.api.Plugin
class RunPlugin implements Plugin<Project> {
void apply(Project project) {
project.android.applicationVariants.all { variant ->
// Task code
}
}
}
為使Gradle能夠找到插件,我們需要提供一個屬性文件。將屬性文件添加到src/main/resources/META-INF/gradle-plugins/
目錄。文件名需要匹配插件的ID。對于RunPlugin
來說,屬性文件名稱為com.gradleforandroid.run.properties
,它的內容是:
implementation-class=com.gradleforandroid.RunPlugin
屬性文件內容僅僅是實現了Plugin接口的類的包名和類名。
準備好接口和屬性文件后,我們可以使用gradlew assemble
命令來構建插件,這會在構建輸出目錄創建一個JAR文件。如果你想將插件上傳到Maven倉庫,你首先需要引入Maven插件:
apply plugin: 'maven'
然后,你需要配置uploadArchives
任務:
uploadArchives {
repositories {
mavenDeployer {
repository(url: uri('repository_url'))
}
}
}
uploadArchives
是預定義的任務。為這個任務配置了倉庫之后,你就可以運行它來發布你的插件。本書不會講述如何設置Maven倉庫。如果你想讓你的插件公開可用,可以發布到Gradleware的插件門戶(https://plugins.gradle.org)。插件門戶有一大批Gradle插件(不僅僅是Android的),如果你想擴展Gradle的默認行為,可以去瀏覽一下。你在https://plugins.gradle.org/docs/submit網站可以學習如何發布插件。
為自定義插件寫測試不是本書的內容,如果你想公開發布插件,強烈建議進行測試。你可以在Gradle用戶指南https://gradle.org/docs/current/userguide/custom_plugins.html#N16CE1學習編寫測試
使用自定義插件
要使用插件,需要在buildscript
塊將其添加為依賴。首先,我們需要配置一個新倉庫。倉庫的配置依賴于插件發布的途徑。其次,我們需要在dependencies
塊配置插件的類路徑。
如果你想引入上例創建的JAR文件,可以定義一個flatDir
倉庫:
buildscript {
repositories {
flatDir { dirs 'build_libs' }
}
dependencies {
classpath 'com.gradleforandroid:plugin'
}
}
如果已經將插件上傳到Maven或者Ivy倉庫,配置會有所不同。我們在第3章講了配置管理,所以我們將不再講述不同之處。
配置好之后,我們需要應用插件:
apply plugin: com.gradleforandroid.RunPlugin
在使用apply()
方法的時候,Gradle會創建一個插件類的實例,并執行插件自己的apply()
方法。