Gradle 學習之插件

本文內容大部分來自 Gradle 官方文檔,英文 OK 的同窗能夠直接看官方文檔。 本文示例代碼已放在 zjxstar 的 GitHubhtml

前言

提及 Gradle 插件,不得不感嘆 Gradle 的設計很是棒。 Gradle 自己提供基本的概念以及核心框架,而其餘的場景邏輯均可以經過插件擴展的方式來實現。對於 Android 開發來講,經常使用的插件就是 Android Gradle 插件和 Java 插件了,咱們會在下一篇文章中詳細介紹 Android 插件。java

本文主要學習 Gradle 插件的基礎知識以及如何編寫自定義插件。android

插件的做用

Gradle 插件一般用來封裝一些可重用的編譯邏輯,能夠用來擴展項目的功能,幫助項目在構建過程當中處理一些特殊邏輯。git

  1. 能夠給項目中添加自定義任務,好比測試、編譯、打包等。
  2. 能夠給項目添加依賴配置,好比第三方庫等。
  3. 能夠向項目中的對象類型添加新的擴展屬性、方法等,從而幫助咱們配置、優化構建,好比 Android 插件的 android{ } 配置塊。
  4. 能夠對項目進行一些約定,好比約定 src/main/java 目錄下存放源代碼。

總之,咱們只要根據插件約定的接口,使用它提供的任務、方法或者擴展,就能夠幫助咱們進行項目構建。github

如何應用一個插件

Gradle 中的插件種類分爲兩種,一種是腳本插件,另外一種是二進制插件。而應用插件的方式也有多種,下面咱們詳細介紹。api

腳本插件

所謂的腳本插件就是咱們自定義的以 .gradle 爲後綴的腳本文件,嚴格意義上來講,它只是一個可執行腳本。應用它就是把整個腳本加載到主 build.gradle 腳本中,從而輔助構建。應用方式以下:網絡

apply from: '腳本文件名' // 好比:apply from: 'project_task_examples.gradle'
複製代碼

它不只能夠應用本地腳本,也能夠應用網絡上的腳本,這樣的話,就必須使用 HTTP URL 了。閉包

舉例說明:app

在前兩章的 Project 和 Task 的學習中,咱們在 app 模塊的 build.gradle 文件中寫了不少示例,咱們把這些示例都提到一個單獨的 gradle 腳本中,而後在 build.gradle 中應用它。新的腳本文件命名: project_task_examples.gradle ,只須要使用 apply from: 'project_task_examples.gradle' 應用便可。框架

project_task_examples.gradle

雖然它不算真正的插件,但也不能忽視它的做用,它是腳本文件模塊化的基礎。咱們能夠把龐大的腳本文件進行分塊、分段整理,拆分紅一個個共用的、職責分明的文件,而後使用 apply from 應用它們。這樣可讓咱們的腳本更加清晰、簡單、方便和快捷。

二進制插件

二進制插件是實現了 org.gradle.api.Plugin 接口的插件(通常都是打包在 jar 裏獨立發佈),它們擁有 plugin id ,這個 id 是插件全局惟一的標識或名稱。咱們須要經過

apply plugin: 'plugin id'
複製代碼

的方式應用二進制插件。好比 Android 的 Application 插件,咱們經過 Android Studio 建立 Android 工程時,在 app 模塊中會自動經過 apply plugin: 'com.android.application' 來引入 Application 插件。

對於 Gradle 自帶的核心插件都有一個容易記的短名,好比 Java 插件,經過 apply plugin:'java' 應用。其實它對應的類型是 org.gradle.api.plugins.JavaPlugin ,因此還能夠經過該類型進行應用:apply plugin:org.gradle.api.plugins.JavaPlugin 。因爲包 org.gradle.api.plugins 是默認導入的,因此咱們能夠去掉包名直接寫爲: apply plugin:JavaPlugin 。

其實 apply 方法有重載方法:

void apply(Closure var1);

void apply(Action<? super ObjectConfigurationAction> var1);

void apply(Map<String, ?> var1);
複製代碼

以前使用的是傳入 Map 參數的方式,咱們也能夠換成閉包的方式。

apply {
    plugin 'java'
}
複製代碼

使用 plugins DSL 應用插件

plugins DSL 是一種新的插件應用方式,Gradle 2.1 以上版本纔可使用。它的語法以下:

plugins {
    id «plugin id»                                            // (1)
    id «plugin id» version «plugin version» [apply «false»]   // (2)
}
複製代碼

方式(1)是提供給 Gradle 核心插件或者構建腳本中已有的插件的。方式(2)提供給須要解析的二進制插件,這些插件要託管在 plugins.gradle.org/ 網站上。

示例:

plugins {
    id 'java'
}

plugins {
    id 'com.jfrog.bintray' version '0.4.1'
}
複製代碼

固然,這種使用方式有必定的限制:

  1. 《plugin id》和《plugin version》必須是常量字符串。
  2. plugins { } 塊必須是 build 腳本中的頂級語句。它不能嵌套在另外一個構造中。
  3. plugins { } 塊目前只能在項目的構建腳本中使用,不能用於腳本插件、settings.gradle 文件或者 init 腳本。

插件的類型和應用方式就介紹到這,下面咱們要學習如何自定義插件,來知足本身的業務邏輯。

自定義插件

自定義插件的關鍵點就是要實現 org.gradle.api.Plugin 接口,重寫 apply 方法來實現本身的業務邏輯。本講中使用的示例都是構建 Task 的簡單示例,感興趣的同窗的能夠自行深刻。

這裏有三種方式來承載自定義插件的代碼:構建腳本、buildSrc 項目、獨立項目。

構建腳本

咱們能夠直接在構建腳本中編寫自定義插件的代碼。 這樣作的好處是插件能夠自動編譯幷包含在構建腳本的類路徑中,而無需執行任何操做。 可是,這樣定義的插件在構建腳本以外是不可見的,即沒法在其定義的構建腳本以外重用該插件。

示例:

/* 自定義插件:直接在腳本文件中定義,其侷限性較大 */
// app的build.gradle文件中
// 定義一個Extension類用來接收腳本傳的參數
class CustomExtensionA {
    String message = 'Hello Custom PluginA'

    // 還能夠定義其餘配置參數
    String greeter = 'Welcome Gradle Plugin'
}

// 定義一個自定義插件類,實現Plugin接口
class CustomPluginA implements Plugin<Project> {

    @Override
    void apply(Project target) {

        // 將Extension註冊給Plugin
        def extension = target.extensions.create('customA', CustomExtensionA)

        // 定義一個任務
        target.task('CustomPluginTaskA') {
            doFirst {
                println 'this is custom plugin A task A'
            }

            doLast {
                // 使用Extension傳入的參數
                println extension.message
                println extension.greeter
            }
        }
    }
}
複製代碼

例子中,咱們建立一個 CustomPluginA 類,實現 Plugin 接口,重寫 apply 方法。在 apply 方法中使用 project 建立一個名爲 CustomPluginTaskA 的任務。該插件還能夠接受擴展,即定義了一個 CustomExtensionA 類,裏面有 message 和 greeter 兩個屬性。在 apply 方法中,經過 project.extensions.create(擴展名, 擴展類) 就能夠完成自定義擴展的註冊。

應用該插件只須要在 build.gradle 文件中使用 apply plugin 引入便可。

// 使用插件CustomPluginA,這裏必須使用插件類名,而不是字符串
// 使用gradlew -q CustomPluginTaskA查看插件是否生效
apply plugin: CustomPluginA

// 配置CustomExtensionA,須要使用註冊名,這裏是customA
customA.message = 'Hi from PluginA'
複製代碼

引入插件後,能夠像運行普通任務同樣經過命令運行插件中定義的 Task 。至於擴展的使用,只需使用擴展名聲明對應屬性便可。

buildSrc 項目

咱們能夠在工程裏新建一個名爲 buildSrc 的模塊來開發自定義插件,其過程和寫 Android Library 相似。buildSrc 模塊中的代碼會自動被 Gradle 加載,Gradle 將負責編譯和測試插件並使其在構建腳本的類路徑中可用。 這種方式定義的插件對於構建中的每一個構建腳本都是可見的。 可是,它在構建以外是不可見的,所以沒法在其定義的構建以外重用該插件。

新建 buildSrc 模塊:

buildSrc自定義插件

該模塊很是簡單,只須要有 src/main/groovy 目錄用來存放源代碼便可(由於使用的 Groovy 語言編寫,因此是 groovy,若是使用 java 或者 kotlin ,換成對應目錄便可)。

在 build.gradle 腳本中依賴 gradleApi 和 Groovy 插件。

// 依賴groovy插件
plugins {
    id 'groovy'
}

dependencies {
    implementation gradleApi()
    implementation localGroovy()
}
複製代碼

接着就是寫代碼了,這個過程和「構建腳本」小節中同樣,只是將代碼放在了 groovy 文件中而已。

// 在單獨的groovy文件中定義插件類
class CustomPluginB implements Plugin<Project> {

    @Override
    void apply(Project project) {
        project.task('CustomPluginTaskB') {
            doFirst {
                println 'This is custom plugin TaskB'
            }
        }
    }
}
複製代碼

最後,就是在 app 模塊中引入該插件。

/* 自定義插件:引用在buildSrc模塊中定義的插件,使用插件類名的全限定名 */
// 使用gradlew -q CustomPluginTaskB查看插件是否生效
apply plugin: com.zjx.happy.plugin.CustomPluginB
複製代碼

因爲構建過程當中 Gradle 會自動加載 buildSrc 模塊中的代碼,因此直接使用插件類的全限定名便可。

獨立項目

咱們能夠爲插件建立單獨的項目。將該項目發佈成一個 JAR ,就能夠在多個地方使用它。這種方式是比較經常使用的。

首先,和 buildSrc 項目同樣建立一個新模塊,模塊名沒有要求。

獨立項目自定義插件

和 buildSrc 不一樣的是,它多出了一個 resources 資源目錄。一樣的,這個模塊裏也要依賴 gradleApi 和 Groovy 插件。

而後,在 groovy 目錄下完成代碼的編寫。這裏依然只是寫個簡單示例。

class CustomPluginC implements Plugin<Project> {

    @Override
    void apply(Project project) {
        project.task('CustomPluginTaskC') {
            doFirst {
                println 'This is Custom Plugin TaskC'
            }
        }
    }
}
複製代碼

至此,插件的核心已經準備好了,如今要給這個插件設置一個 plugin id 。須要在 resources 目錄下,建立 META-INF 目錄。而後在 META-INF 目錄下再建立一個 gradle-plugins 目錄。( 注意: 這裏建立目錄時請逐級建立。不要經過一次性輸入 META-INF.gradle-plugins 的目錄名方式建立,由於 resources 目錄沒法像代碼目錄那樣自動識別包名並分級,它只會建立一個目錄,這樣是不正確的。)

目錄建立好後,咱們須要在該目錄下,新建一個 properties 文件,而文件名就是插件的 plugin id 。該 properties 文件中須要聲明插件的實現類,語法以下:

# 建立該目錄時必定要注意,要一級一級的建立,先建立META-INF,再建立gradle-plugins,而不是直接META-INF.gradle-plugins
# 由於不是src的目錄,因此沒法自動識別 . 號
# implementation-class是固定的,等號右邊是插件實現類的全限定類名
implementation-class=com.zjx.happy.plugin.CustomPluginC
複製代碼

詳細目錄結構如圖:

resources目錄

本示例中的插件的 plugin id 就是 com.happy.custompluginc 。那麼 plugin id 有哪些規範呢?

  • 能夠包含任何字母、數字、.-
  • 必須至少包含一個 . 將命名空間和插件名分開
  • 一般使用小寫反向域名來約定命名空間
  • 插件名僅使用小寫字符
  • 命名空間 org.gradle 和 com.gradleware 不可使用;
  • 不能以 . 開頭和結尾
  • 不能使用連續的 . ,好比: ..

插件準備好後,咱們要將其發佈出去,通常使用 Maven 。

// build.gradle中
apply plugin: 'maven' // 引入 maven 插件

repositories {
    mavenCentral()
}

// 將該插件上傳到本地Maven庫
group='com.happy.plugin'
version='1.0.0'

// 經過gradlew :customPlugin:uploadArchives命令上傳
uploadArchives {
    repositories {
        mavenDeployer {
            //本地的Maven地址設置爲../repos
            repository(url: uri('../repos'))
        }
    }
}
複製代碼

使用 gradlew :customPlugin:uploadArchives 命令上傳。示例中會在工程目錄下建立 repos 目錄,裏面存放了 customPlugin 的 jar 包和 pom 信息。

repos目錄

如何應用獨立模塊自定義的插件呢?其方法和 Android 插件同樣。

在根 build.gradle 文件中聲明插件依賴:

dependencies {
    	// 這是Android插件
        classpath 'com.android.tools.build:gradle:3.3.1'
		// 這是剛自定義的插件
        classpath 'com.happy.plugin:customPlugin:1.0.0'
    }
複製代碼

同步以後,就能夠在 app 模塊中使用 apply plugin 引入了。

/* 自定義插件:引用已經發布到Maven的插件 */
apply plugin: 'com.happy.custompluginc' // 這個plugin id就是properties文件的文件名
複製代碼

最後,使用命令 gradlew -q CustomPluginTaskC 驗證插件是否生效。

總結

Gradle 插件相關的內容就這麼多,總體上看仍是很簡單的。如今社區中的第三方插件不少,咱們能夠根據業務須要適當引用。固然,咱們也徹底能夠本身實現適合業務的插件。通常 APM 類項目中 中就須要實現自定義插件來完成代碼的自動插樁,它須要利用到 Android 提供的 Gradle-api 中的 Transform 。後續的文章中咱們會學習 Transform 。

參考資料

  1. Gradle 官方文檔-使用插件
  2. Gradle 官方文檔-自定義插件
  3. 《Android Gradle權威指南》
  4. 《Gradle for Android 中文版》
相關文章
相關標籤/搜索