天天看點

暴力突破 Gradle 自動化項目建構(七)- 其他子產品及自定義 Gradle 插件一、前言二、SourceSet三、Gradle Plugin(插件)

一、前言

 Gradle 子產品分為如下四部分:

暴力突破 Gradle 自動化項目建構(七)- 其他子產品及自定義 Gradle 插件一、前言二、SourceSet三、Gradle Plugin(插件)

 其中 Setting 類在之前的篇幅中已經學習過了,Android 對 gradle 的擴充我們放到下一篇來學習。這篇我們先來學習 SourceSet 以及自定義 Plugin 插件。

二、SourceSet

SourceSet 主要是用來設定我們項目中源碼或資源的位置的,目前它最常見的兩個使用案例就是如下兩類:

  • 修改 so 庫存放位置。
  • 資源檔案分包存放。
暴力突破 Gradle 自動化項目建構(七)- 其他子產品及自定義 Gradle 插件一、前言二、SourceSet三、Gradle Plugin(插件)

此外,我們也可以使用如下代碼将 sourceSets 在 android 閉包的外部進行定義:

暴力突破 Gradle 自動化項目建構(七)- 其他子產品及自定義 Gradle 插件一、前言二、SourceSet三、Gradle Plugin(插件)

三、Gradle Plugin(插件)

自定義 Gradle 插件的本質就是把邏輯獨立的代碼進行抽取和封裝,以便于我們更高效地通過插件依賴這一方式進行功能複用。而在 Android 下的 gradle 插件共分為兩大類,如下所示:

  1. 腳本插件:同普通的 gradle 腳本編寫形式一樣,通過 apply from: 'test.gradle' 引用。
  2. 對象插件:通過插件全路徑類名或 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'
           

目錄結構如下:

暴力突破 Gradle 自動化項目建構(七)- 其他子產品及自定義 Gradle 插件一、前言二、SourceSet三、Gradle Plugin(插件)

3.2.3 在獨立工程下

在 buildSrc 下建立的 plugin 隻能在該工程下的多個子產品之間複用代碼。如果想要在多個項目之間複用這個插件,我們就需要在一個單獨的工程中編寫插件,将編譯後的 jar 包上傳到 maven 倉庫。這裡為了不增加複雜度,我們還是在該工程下建立一個 standaloneplugin 子產品(java library module)。隻需要明白我們完全可以在一個獨立的工程下來編寫插件。我們看下建立好後的目錄結構:

暴力突破 Gradle 自動化項目建構(七)- 其他子產品及自定義 Gradle 插件一、前言二、SourceSet三、Gradle Plugin(插件)

從目錄結構來看,和 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 :

暴力突破 Gradle 自動化項目建構(七)- 其他子產品及自定義 Gradle 插件一、前言二、SourceSet三、Gradle Plugin(插件)

可以看到正常列印了我們配置的 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:

暴力突破 Gradle 自動化項目建構(七)- 其他子產品及自定義 Gradle 插件一、前言二、SourceSet三、Gradle Plugin(插件)

繼續閱讀