一、前言
Gradle 子產品分為如下四部分:
其中 Setting 類在之前的篇幅中已經學習過了,Android 對 gradle 的擴充我們放到下一篇來學習。這篇我們先來學習 SourceSet 以及自定義 Plugin 插件。
二、SourceSet
SourceSet 主要是用來設定我們項目中源碼或資源的位置的,目前它最常見的兩個使用案例就是如下兩類:
- 修改 so 庫存放位置。
- 資源檔案分包存放。
此外,我們也可以使用如下代碼将 sourceSets 在 android 閉包的外部進行定義:
三、Gradle Plugin(插件)
自定義 Gradle 插件的本質就是把邏輯獨立的代碼進行抽取和封裝,以便于我們更高效地通過插件依賴這一方式進行功能複用。而在 Android 下的 gradle 插件共分為兩大類,如下所示:
- 腳本插件:同普通的 gradle 腳本編寫形式一樣,通過 apply from: 'test.gradle' 引用。
- 對象插件:通過插件全路徑類名或 id 引用。
3.1 腳本插件
腳本插件就是一個普通的 gradle 建構腳本,我們既可以寫在 build.gradle 裡面,也可以自己建立一個 gradle 腳本檔案進行編寫。如果是建立一個 gradle 腳本,則需要通過 apply from 引用。
//test.gradle
project.task("showConfig") {
doLast {
println("$project.name:showConfig")
}
}
//build.gradle
apply from: '../test.gradle'
3.2 對象插件
對象插件是指實作了 org.gradle.api.Plugin 接口的類。Plugin 接口需要實作 void apply(T target) 這個方法。該方法中的泛型指的是此 Plugin 可以應用到的對象,而我們通常是将其應用到 Project 對象上。編寫對象插件主要有三種方式:
- 直接在gradle腳本檔案中
- 在buildSrc目錄下
- 在獨立的項目下
3.2.1 在gradle腳本檔案中
直接在 gradle 腳本中編寫這個方式是最為簡單的。打開 app.gradle 檔案,在其中編寫一個類實作 Plugin 接口:
//app.gradle
class CustomPluginInBuildGradle implements Plugin<Project> {
@Override
void apply(Project target) {
target.task('showCustomPluginInBuildGradle'){
doLast {
println("task in CustomPluginInBuildGradle")
}
}
}
}
然後通過插件類名引用它:
//app.gradle
apply plugin: CustomPluginInBuildGradle
3.2.2 在buildSrc目錄下
除了直接寫在某個子產品的建構腳本中,我們還可以将插件寫在工程根目錄下的 buildSrc 目錄下,這樣可以在多個子產品之間複用該插件。雖然 buildSrc 是 Gradle 在項目中配置自定義插件的預設目錄,但它并不是标準的 Android 工程目錄,是以使用這種方式需要我們事先手動建立一個 java library module,該 module 必須命名為 buildSrc。将 src/main/java 改成 src/main/groovy。
在 buildSrc 目錄下建立一個 build.gradle,并在 build.gradle 中引用 groovy 插件:
apply plugin: 'groovy'
dependencies {
compile gradleApi()
compile localGroovy()
}
然後建立一個 xxxPlugin.groovy 并實作 Plugin 接口,例如:
package com.lerendan.buildsrc
import org.gradle.api.Plugin
import org.gradle.api.Project
class TestPlugin implements Plugin<Project> {
@Override
void apply(Project project) {
project.task('pluginTest') {
doLast {
println 'Hello World'
}
}
}
}
可以看到,上述 plugin 僅是在 apply() 方法内部建立了一個名為 pluginTest 的 task。由于 buildSrc 目錄是 gradle 預設的目錄之一,該目錄下的代碼會在建構是自動編譯打包,并被添加到 buildScript 中的 classpath 下,是以不需要任何額外的配置,就可以直接被其他子產品的建構腳本所引用。
通過類名引用插件的需要使用全限定名,也就是需要帶上包名,或者可以先導入這個插件類,如下:
//app.gradle
apply plugin: com.lerendan.buildsrc.TestPlugin
注意這裡引用的方式可以是通過類名引用,也可以通過給插件映射一個 id,然後通過 id 引用。通過簡單的 id 的方式,我們可以隐藏類名等細節,使的引用更加容易。映射方式很簡單,在 buildSrc目錄下建立 resources/META-INF/gradle-plugins/xxx.properties,這裡的 xxx 也就是所映射的 id,這裡我們假設取名myplugin。myplugin.properties 檔案中配置該 id 所對應的 plugin 實作類:
implementation-class=com.lerendan.buildsrc.TestPlugin
此時就可以通過 id 來引用對象的插件了:
//app.gradle
apply plugin: 'myplugin'
目錄結構如下:
3.2.3 在獨立工程下
在 buildSrc 下建立的 plugin 隻能在該工程下的多個子產品之間複用代碼。如果想要在多個項目之間複用這個插件,我們就需要在一個單獨的工程中編寫插件,将編譯後的 jar 包上傳到 maven 倉庫。這裡為了不增加複雜度,我們還是在該工程下建立一個 standaloneplugin 子產品(java library module)。隻需要明白我們完全可以在一個獨立的工程下來編寫插件。我們看下建立好後的目錄結構:
從目錄結構來看,和 buildSrc 目錄是一緻的。差別在于 buildSrc 下的代碼在建構時會自動編譯并被引用。而我們在獨立項目中編寫的插件如果要能正确的被引用到,需要上傳到 maven 倉庫中,然後顯式地在需要引用的項目中的 buildSrcipt 中添加對該構件的依賴。
接下來我們來完成插件代碼:
package com.lerendan.aloneplugin
import org.gradle.api.Plugin
import org.gradle.api.Project
class AlonePlugin implements Plugin<Project> {
@Override
void apply(Project project) {
project.task('showAlonePlugin') {
doLast {
println('task in AlonePlugin')
}
}
}
}
在 alonePlugin 目錄下建立一個 build.gradle,并在 build.gradle 中引用插件項目建構腳本:
//alonePlugin build.gradle
apply plugin: 'groovy'
apply plugin: 'maven'
dependencies {
compile gradleApi()
compile localGroovy()
}
group = 'com.lerendan.aloneplugin'
version = '1.0.0'
uploadArchives {
repositories {
mavenDeployer {
repository(url: uri('../repo'))
}
}
}
這裡與 buildSrc 不同的是,我們引用了 apply plugin 'maven',通過 maven 插件,我們可以輕松的配置 group,version 以及 uploadArchives 的相關屬性,然後執行 gradlew uploadArchives 這個任務,就可以将構件打包後上傳到 maven 倉庫了。同樣為了示例簡單,我們上傳到一個本地倉庫 repository(url: uri('../repo')) 中。
上傳之後就可以在項目根目錄下找到 repo 這個目錄了。最後我們通過給根目錄下的 build.gradle 配置 buildScript 的 classpath,就可以引用這個插件了。注意,classpath 的格式為 group:artifact:version。
//根目錄下 build.gradle
buildscript {
repositories {
maven {
url uri('repo')
}
jcenter()
google()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.3.2'
classpath 'com.lerendan.aloneplugin:alonePlugin:1.0.0'
}
}
引用插件:
//app.gradle
apply plugin: 'testAlonePlugin'
3.3 對象插件中自定義 Extension 與 Task
3.3.1 自定義 Extension
下面我們來看下自定義 Extension 的步驟,首先建立一個實體類:
//ReleaseInfoExtension.groovy
package com.lerendan.buildsrc
class ReleaseInfoExtension{
String versionName
String versionCode
String versionInfo
@Override
String toString() {
return "versionName = $versionName , versionCode = $versionCode , versionInfo = $versionInfo"
}
}
接着我們在 buildSrc 中建立一個 Gradle 插件:
//TestPlugin.groovy
package com.lerendan.buildsrc
import org.gradle.api.Plugin
import org.gradle.api.Project
class TestPlugin implements Plugin<Project> {
@Override
void apply(Project project) {
project.extensions.create("releaseInfo", ReleaseInfoExtension.class)
project.task('pluginTest') {
doLast {
ReleaseInfoExtension releaseInfo = project.releaseInfo
println releaseInfo
}
}
}
}
可以看到,我們通過 project.extensions.create 建立了這樣一個自定義的 Extension。接着我們在 app module 的 build.gradle 中引入這個 plugin,就可以在 app moudle 的 build.gradle 腳本中使用 releaseInfo 去配置擴充屬性,代碼如下所示:
//app module 的 build.gradle
apply plugin: com.lerendan.buildsrc.TestPlugin
releaseInfo {
versionCode = "1"
versionName = "1.0.0"
versionInfo = "第一個版本~"
}
......
執行這個 task :
可以看到正常列印了我們配置的 releaseInfo。
3.3.2 自定義 Task
使用自定義擴充屬性 Extension 僅僅是為了讓使用插件者有配置插件的能力。而插件還得借助自定義 Task 來實作相應的功能,必須繼承 DefaultTask。這裡我們建立一個 Task,其具體實作代碼如下所示:
//ReleaseInfoTask.groovy
package com.lerendan.buildsrc
import org.gradle.api.DefaultTask
import org.gradle.api.tasks.TaskAction
class ReleaseInfoTask extends DefaultTask {
ReleaseInfoTask() {
// 1、在構造器中配置了該 Task 對應的 Task group,即 Task 組,并為其添加上了對應的描述資訊。
group = 'version_manager'
description = 'release info update'
}
// 2、在 gradle 執行階段執行
@TaskAction
void doAction() {
updateVersionInfo()
}
private void updateVersionInfo() {
// 3、從 realeaseInfo Extension 屬性中擷取相應的版本資訊
ReleaseInfoExtension releaseInfoExtension = project.extensions.releaseInfo
println releaseInfoExtension
}
}
首先我們在構造器中配置了該 Task 對應的 Task group,即 Task 組,并為其添加上了對應的描述資訊。接着,我們使用了 @TaskAction 注解标注了 doAction 方法,這樣這個方法就會在 gradle 執行階段執行。最後可以使用 project.extensions.releaseInfo 從 realeaseInfo Extension 屬性中了擷取相應的配置資訊。
可以看到,自定義的插件 task 都會遵循這個步驟,當然,最後别忘了在我們的 TestPlugin 的 apply 方法中加入下面代碼去建立 ReleaseInfoTask 執行個體,代碼如下所示:
//TestPlugin.groovy
package com.lerendan.buildsrc
import org.gradle.api.Plugin
import org.gradle.api.Project
class TestPlugin implements Plugin<Project> {
@Override
void apply(Project project) {
// 建立extension
project.extensions.create("releaseInfo", ReleaseInfoExtension.class)
// 建立task
project.tasks.create("releaseInfoTask", ReleaseInfoTask.class)
}
}
執行 releaseInfoTask 這個 task: