Gradle入門系列(五)——Gradle其它模組與Plugin外掛

GitLqr發表於2019-01-05

Gradle其它模組

一、Settings類

settings.gradle(對應Settings.java)決定哪些工程需要被gradle處理,佔用了整個gradle生命週期的三分之一,即Initialzation初始化階段。

二、SourceSet類

對預設的檔案位置進行修改,從而讓gradle知道哪種資源要從哪些資料夾中去查詢。

// sourceSets是可以呼叫多次的
android {
    sourceSets {
        main {
            jniLibs.srcDirs = ['libs']
        }
    }
    sourceSets {
        main {
            res.srcDirs = ['src/main/res',
                           'src/main/res-ad',
                           'src/main/res-player']
        }
    }
}

// sourceSets一般情況下是一次性配置
android {
    sourceSets {
        main {
            jniLibs.srcDirs = ['libs']
            res.srcDirs = ['src/main/res',
                           'src/main/res-ad',
                           'src/main/res-player']
        }
    }
}

// 使用程式設計的思想,配置sourceSets
this.android.sourceSets{
    main {
        jniLibs.srcDirs = ['libs']
        res.srcDirs = ['src/main/res',
                       'src/main/res-ad',
                       'src/main/res-player']
    }
}
複製程式碼

Gradle Plugin

一、Gradle外掛(Plugin)是什麼

Gradle中的Plugin是對完成指定功能的Task封裝的體現,只要工程依賴了某個Plugin,就能執行該Plugin中所有的功能,如:使用java外掛,就可以打出jar包,使用Android外掛,就可以生成apk、aar。

二、自定義Plugin

1、建立外掛工程

  1. 在工程目錄下建立buildSrc資料夾。
  2. 在buildSrc目錄下,建立src資料夾、build.gradle檔案。
  3. 在buildSrc/src目錄下,再建立main資料夾。
  4. 在buildSrc/src/main目錄下,再分別建立groovy、resources資料夾。
  5. 在buildSrc/src/main/resources再建立一個META-INF資料夾,再在META-INF下建立一個gradle-plugins資料夾。
  6. 在build.gradel檔案中輸入如下指令碼:
apply plugin: 'groovy'

sourceSets {
    main {
        groovy {
            srcDir 'src/main/groovy'
        }
        resources {
            srcDir 'src/main/resources'
        }
    }
}
複製程式碼

最後,Async一下工程,buildSrc就會被識別出來了,整體目錄如圖:

Gradle入門系列(五)——Gradle其它模組與Plugin外掛

2、建立外掛類

與java一樣,在groovy目錄下,建立一個包,再建立一個外掛類(如:com.lqr.gradle.study.GradleStudyPlugin),該外掛類必須實現Plugin介面。

注意:gradle外掛類是.groovy檔案,不是.java檔案

import org.gradle.api.Plugin
import org.gradle.api.Project

/**
 * 自定義Gradle外掛
 */
class GradleStudyPlugin implements Plugin<Project> {

    /**
     * 外掛引入時要執行的方法
     * @param project 引入當前外掛的project
     */
    @Override
    void apply(Project project) {
        println 'hello gradle study plugin. current project name is ' + project.name
    }
}
複製程式碼

3、指定外掛入口

在編寫完外掛類的邏輯之後,需要在META-INF.gradle-plugins目錄下建立一個properties檔案(建議以外掛類包名來命名,如:com.lqr.gradle.study.properties),在該properties中宣告外掛類,以此來指定外掛入口。

該properties檔案的名字將作為當前gradle外掛被app工程引用的依據。

implementation-class=com.lqr.gradle.study.GradleStudyPlugin
// 如果報錯 Could not find implementation class 'xxx' 的話,一般是類全路徑有問題,預設包不需要寫包路徑,修改如下即可:
// implementation-class=GradleStudyPlugin
複製程式碼

4、使用自定義外掛

開啟app工程的build.gradle,應用上面的自定義gradle外掛,並Async。

apply plugin: 'com.android.application'
apply plugin: 'com.lqr.gradle.study'

android {
  ...
}
複製程式碼

可以看到,在gradle的配置階段,就輸出了前面自定義外掛的apply方法中的日誌。

Gradle入門系列(五)——Gradle其它模組與Plugin外掛

5、建立擴充套件屬性

外掛往往會在gradle指令碼中進行引數配置,如在android{}中,可以配置compileSdkVersion等引數,其實本質上,就是在gradle指令碼中使用閉包方式建立了一個javaBean,並將其傳遞到外掛中被外掛識別讀取而已。步驟如下:

1)建立一個實體類,宣告成員變數,用於接收gradle中配置的引數。(可以理解為就是javaBean,不過要注意,該檔案字尾是.groovy,不是.java)

class ReleaseInfoExtension {
    String versionCode
    String versionName
    String versionInfo
    String fileName

    ReleaseInfoExtension() {}

    @Override
    String toString() {
        return "versionCode = ${versionCode} , versionName = ${versionName} ," +
                " versionInfo = ${versionInfo} , fileName = ${fileName}"
    }
}
複製程式碼

2)在自定義外掛中,對當前project進行擴充套件。

class GradleStudyPlugin implements Plugin<Project> {

    /**
     * 外掛引入時要執行的方法
     * @param project 引入當前外掛的project
     */
    @Override
    void apply(Project project) {
        // 這樣就可以在gradle指令碼中,通過releaseInfo閉包來完成ReleaseInfoExtension的初始化。
        project.extensions.create("releaseInfo", ReleaseInfoExtension)
    }
}
複製程式碼

3)開啟在app工程的build.gradle,通過擴充套件key值命名閉包的方式,就可以配置指定引數了。

apply plugin: 'com.lqr.gradle.study'

releaseInfo {
    versionCode = '1.0.0'
    versionName = '100'
    versionInfo = '第一個app資訊'
    fileName = 'release.xml'
}
複製程式碼

4)接收引數,如:

def versionCodeMsg = project.extensions.releaseInfo.versionCode
複製程式碼

6、建立擴充套件Task

自定義外掛無非就是封裝一些常用Task,所以,擴充套件Task才是自定義外掛的最重要的一部分。

擴充套件Task也很簡單,繼承DefaultTask,編寫TaskAction註解方法,下面以 “把app版本資訊寫入到xml檔案中”的task 為例,註釋很詳細,不多贅述:

import groovy.xml.MarkupBuilder
import org.gradle.api.DefaultTask
import org.gradle.api.tasks.TaskAction

class ReleaseInfoTask extends DefaultTask {

    ReleaseInfoTask() {
        group 'lqr' // 指定分組
        description 'update the release info' // 新增說明資訊
    }

    /**
     * 使用TaskAction註解,可以讓方法在gradle的執行階段去執行。
     * doFirst其實就是在外部為@TaskAction的最前面新增執行邏輯。
     * 而doLast則是在外部為@TaskAction的最後面新增執行邏輯。
     */
    @TaskAction
    void doAction() {
        updateInfo()
    }

    private void updateInfo() {
        // 獲取gradle指令碼中配置的引數
        def versionCodeMsg = project.extensions.releaseInfo.versionCode
        def versionNameMsg = project.extensions.releaseInfo.versionName
        def versionInfoMsg = project.extensions.releaseInfo.versionInfo
        def fileName = project.extensions.releaseInfo.fileName
        // 建立xml檔案
        def file = project.file(fileName)
        if (file != null && !file.exists()) {
            file.createNewFile()
        }
        // 建立寫入xml資料所需要的類。
        def sw = new StringWriter();
        def xmlBuilder = new MarkupBuilder(sw)
        // 若xml檔案中沒有內容,就多建立一個realease節點,並寫入xml資料
        if (file.text != null && file.text.size() <= 0) {
            xmlBuilder.releases {
                release {
                    versionCode(versionCodeMsg)
                    versionName(versionNameMsg)
                    versionInfo(versionInfoMsg)
                }
            }
            file.withWriter { writer ->
                writer.append(sw.toString())
            }
        } else { // 若xml檔案中已經有內容,則在原來的內容上追加。
            xmlBuilder.release {
                versionCode(versionCodeMsg)
                versionName(versionNameMsg)
                versionInfo(versionInfoMsg)
            }
            def lines = file.readLines()
            def lengths = lines.size() - 1
            file.withWriter { writer ->
                lines.eachWithIndex { String line, int index ->
                    if (index != lengths) {
                        writer.append(line + '\r\n')
                    } else if (index == lengths) {
                        writer.append(sw.toString() + '\r\n')
                        writer.append(line + '\r\n')
                    }
                }
            }
        }
    }
}
複製程式碼

與建立擴充套件屬性一樣,擴充套件Task也需要在project中建立注入。

/**
 * 自定義Gradle外掛
 */
class GradleStudyPlugin implements Plugin<Project> {

    /**
     * 外掛引入時要執行的方法
     * @param project 引入當前外掛的project
     */
    @Override
    void apply(Project project) {
        // 建立擴充套件屬性
        // 這樣就可以在gradle指令碼中,通過releaseInfo閉包來完成ReleaseInfoExtension的初始化。
        project.extensions.create("releaseInfo", ReleaseInfoExtension)
        // 建立Task
        project.tasks.create("updateReleaseInfo", ReleaseInfoTask)
    }
}
複製程式碼

再次Async工程之後,就可以在Idea的gradle標籤裡看到自定義好的Task了。

Gradle入門系列(五)——Gradle其它模組與Plugin外掛

以上就是自定義gradle外掛的核心內容了,但是,這種在工程下直接建立buildSrc目錄編寫的外掛,只能對當前工程可見,所以,如果需要將我們自定義好的grdle外掛被其他工程所使用,則需要單獨建立一個庫工程,並建立如buildSrc目錄下所有的檔案,最後上傳maven倉庫即可,這部分可自行百度瞭解。

三、android外掛對gradle擴充套件

譯者序 | Gradle Android外掛使用者指南翻譯

Manipulation tasks(操作task) | Gradle Android外掛使用者指南翻譯

自定義Apk輸出位置:

this.afterEvaluate {
  this.android.applicationVariants.all { variant ->
    def output = variant.outpus.first() // 獲取變體輸出檔案(outputs返回是一個集合,但只有一個元素,即輸出apk的file)
    def apkName = "app-${variant.baseName}-${variant.versionName}.apk"
    output.outputFile = new File(output.outputFile.parent, apkName)
  }
}
複製程式碼

variant.baseName : baidu-release variant.name : baiduRelease

相關文章