Gradle理論與實踐四:自定義Gradle外掛

dead_lee發表於2021-09-09

Gradle外掛

Gradle可以認為是一個框架,負責定義流程和規則。而具體的編譯工作則是透過外掛的方式來完成的。比如編譯 Java 有 Java 外掛,編譯 Groovy 有 Groovy 外掛,編譯 Android APP 有 Android APP 外掛,編譯 Android Library 有 Android Library 外掛。在Gradle中一般有兩種型別的外掛,指令碼外掛二進位制外掛。使用外掛方式可以使得同一邏輯在專案中複用,也可以針對不同專案做個性化配置,只要外掛程式碼支援即可。

一、Java Gradle外掛

Java外掛引入方式:

  • apply plugin: 'java'

Java外掛約定src/main/java為我們專案原始碼存放位置;src/main/resources為資源存放位置;src/test/java為我們單元測試用例存放目錄;src/test/resources存放我們單元測試中資源存放位置。
java外掛引入了一個概念叫做SourceSets,透過修改SourceSets中的屬性,可以指定哪些原始檔(或資料夾下的原始檔)要被編譯,哪些原始檔要被排除。Gradle就是透過它實現Java專案的佈局定義。

預設配置:

android {
    sourceSets {
        main {
            manifest.srcFile 'AndroidManifest.xml'
            java.srcDirs = ['src']
            resources.srcDirs = ['src']
            aidl.srcDirs = ['src']
            renderscript.srcDirs = ['src']
            res.srcDirs = ['res']
            assets.srcDirs = ['assets']
            jniLibs.srcDirs = ['libs']
        }

}

如果想修改原始碼的目錄以及多個resource的目錄,可以透過下面來設定:

  sourceSets {
        main {
            java.srcDirs = ['other/java']
            res.srcDirs =
                    [                            'src/main/res/',                            'src/main/res/extra'
                    ]
        }
    }

更多設定請移步官網:
https://developer.android.com/studio/build/build-variants

二、Android Gradle外掛

Android其實就是Gradle的一個第三方外掛,Android Gradle和Android Studio完美無縫搭配的新一代構建系統。

  • APP外掛id :com.android.application

  • Library外掛id:com.android.library

  • Test外掛id:com.android.test

2.1、應用Android Gradle外掛

上面說了Android Gradle是Gradle的一個三方外掛,託管在jcenter上,如果要使用,必須知道他們的外掛id,另外還要配置他們依賴的classpath,在根目錄的build.gradle中配置如下:

buildscript {
    
    repositories {        //程式碼倉庫
        jcenter()
    }
    dependencies {        //Android Gradle外掛版本
        classpath 'com.android.tools.build:gradle:2.3.3'
    }
}

配置程式碼倉庫為jcenter,當編譯專案時,Gradle會去jcenter倉庫中尋找Android Gradle對應版本的依賴,以上配置好後,就可以使用Android Gradle外掛了,在我們app目錄的build.gradle下:

apply plugin: 'com.android.application

android {
    compileSdkVersion 26 
    buildToolsVersion "26.0.3"

    defaultConfig {
        applicationId "org.ninetripods.qrcode"
        minSdkVersion 14
        targetSdkVersion 26
        versionCode 1
        versionName "1.0"
    }

    signingConfigs {
        release {
            keyAlias 'xxx'
            keyPassword 'xxx'
            storeFile file('xxx.jks')
            storePassword 'xxx'
        }
    }

    buildTypes {
        release {
            minifyEnabled false
            signingConfig signingConfigs.release
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}

android{}是Android Gradle外掛提供的一個擴充套件型別,可以讓我們自定義Android Gradle工程。

  • compileSdkVersion: compileSdkVersion告訴Gradle用哪個Android SDK版本編譯應用
    1、應用想相容新版本、使用了新版本API,此時就必須使用新版本及以上版本編譯,否則就會編譯報錯;
    2、如果使用了新版本的Support Library,此時也必須使用新版本及以上版本編譯

  • buildToolsVersion:構建該Android工程所用構建工具(aapt, dx, renderscript compiler, etc...)的版本,一般google在釋出新的SDK時,會同時釋出對應的buildToolsVersion,更詳細的見:
    PS:Android Studio 3.0以上時,buildToolsVersion不是必須要寫的了。

  • defaultConfig: 預設配置,它是一個ProductFlavor。ProductFlavor允許我們在打多渠道包時根據不同的情況生成不同的APK包。即如果不在ProductFlavor中單獨配置的話,那麼會使用defaultConfig的預設配置。

  • minSdkVersion: 最低支援的Android系統API Level

  • targetSdkVersion: App基於哪個Android版本開發的

  • versionCode: App應用內部版本號,一般用來控制App升級

  • versionName:  App應用的版本名稱,即我們釋出的App版本,一般使用者可以看到。

minSdkVersion、targetSdkVersion、compileSdkVersion三者的關係:
minSdkVersion <= targetSdkVersion <= compileSdkVersion

理想狀態是:
minSdkVersion(lowest possible)  <= targetSdkVersion == compileSdkVersion(latest SDK)

三、自定義Gradle外掛

編寫自定義Gradle外掛原始碼的有下面三個地方:

3.1、Build script

可以在構建指令碼中直接編寫自定義外掛的原始碼。這樣做的好處是外掛可以自動編譯幷包含在構建指令碼的classpath中,不需要再去宣告。然而,這個自定義外掛在構建指令碼之外是不可見的,因此這種方式實現的外掛在構建指令碼之外是不能複用。
舉個例子,在根目錄下的build.gradle中寫入:

//build.gradleclass GreetingPlugin implements Plugin<Project> { @Override
 void apply(Project project) {     //新建task hello
     project.task('hello') {
         doLast {
             println 'Hello from the GreetingPlugin'
         }
     }
 }
}//引入外掛apply plugin: GreetingPlugin

執行結果

./gradlew hello
Hello from the GreetingPlugin

3.2、buildSrc project

在專案的根目錄下新建一個buildSrc/src/main/groovy的目錄,將自定義外掛的原始碼放入此目錄中,Gradle將負責編譯和測試外掛,並使其在構建指令碼的classpath中可見。這個外掛對於專案中所有的build script都是可見的,但是在構建之外是不可見的,此構建方式不能再其他專案中複用。
舉例:在rootProjectDir/buildSrc/src/main/groovy目錄下新建了一個外掛類,如下:


圖片描述

image.png

GreetingExtensionPlugin.groovy中程式碼如下:

//GreetingExtensionPlugin.groovypackage comimport org.gradle.api.Pluginimport org.gradle.api.Projectclass GreetingExtensionPlugin implements Plugin<Project> {    @Override
    void apply(Project project) {        // Add the 'greeting' extension object
        def extension = project.extensions.create('greeting', GreetingExtension)        // Add a task that uses configuration from the extension object
        project.tasks.create('buildSrc') {
            doLast {
                println "${extension.message} from ${extension.greeter}"
                println project.greeting
            }
        }
    }
}class GreetingExtension {
    String message
    String greeter
}

定義好的外掛就可以在專案中所有的build.gradle中使用了:

//build.gradleapply plugin: GreetingExtensionPlugin

greeting {
    message = 'hello'
    greeter = 'GreetingExtensionPlugin'}

執行結果:

./gradlew buildSrc
hello from GreetingExtensionPlugin
com.GreetingExtension_Decorated@42870556
  • 擴充套件屬性:自定義外掛程式碼中有一句def extension = project.extensions.create('greeting', GreetingExtension),可以用來擴充套件屬性,可以理解為往GreetingExtension中新加了一個屬性。建立完成後,可以透過project.greeting來獲取擴充套件屬性的例項。使用這種方式來給外掛傳遞引數。

3.3、Standalone project

上面兩種自定義外掛都只能在自己的專案中使用,如果想在其他專案中也能複用,可以建立一個單獨的專案並把這個專案釋出成一個JAR,這樣多個專案中就可以引入並共享這個JAR。通常這個JAR包含一些外掛,或者將幾個相關的task捆綁到一個庫中,或者是外掛和task的組合, Standalone project建立步驟:

  • 在Android Studio的rootProject目錄下新建一個Module,型別隨便選一個就行(如 Android Module),後面會有大的改動。(也可以選擇IDEA來開發,IDEA中可以直接建立groovy元件)

  • 清空Module目錄下build.gradle中的所有內容,刪除其他所有檔案

  • 在Module中建立src/main/groovy的目錄,然後再建立包名資料夾。在main目錄下再新建resources/META-INF/gradle-plugins目錄,在這個目錄下編寫一個和外掛id名字相同的.properties檔案,這樣Gradle就可以找到外掛實現了。

舉個例子,下面的程式碼實現了在build目錄中新建個文字檔案,並寫入文字的功能。
1、在src/main/groovy下新建/com/fastgo/plugin目錄並建立一個名為CustomPlugin.groovy的檔案:

package com.fastgo.pluginimport org.gradle.api.DefaultTaskimport org.gradle.api.Projectimport org.gradle.api.Pluginimport org.gradle.api.tasks.TaskActionclass CustomPlugin implements Plugin<Project> {    @Override
    void apply(Project project) {
        project.tasks.create('writeToFile', CustomPluginTask) {
            destination = { project.greetingFile }
            doLast {
                println project.file(destination).text
            }
        }
    }
}class CustomPluginTask extends DefaultTask {    def destination
    File getDestination() {        //建立路徑為destination的file
        project.file(destination)
    }    @TaskAction
    def greet() {
        def file = getDestination()
        file.parentFile.mkdirs()        //向檔案中寫入文字
        file.write('hello world')
    }
}

上面的程式碼中在外掛的apply(Project project)中建立了名為writeToFile的Task,並依賴於CustomPluginTaskCustomPluginTask中定義了一個destination路徑,並透過project.file(destination)建立建立一個路徑為destination的檔案,並往檔案中寫入文字。

注意:別忘了引入 package com.fastgo.plugin,否則最後生成後會提示找不到外掛。

2、建立resources/META-INF/gradle-plugins/com.fastgo.plugin.properties檔案,並在檔案裡寫入:

implementation-class=com.fastgo.plugin.CustomPlugin

3、在build.gradle中寫入:

plugins {
    id 'groovy'
    id 'maven-publish'
    id 'maven'}

dependencies {
    implementation gradleApi()
    implementation localGroovy()
}

repositories {
    mavenCentral()
}

group = 'com.fastgo'version = '1.0-test'publishing {
    repositories {
        maven {
            url = uri("$rootDir/repo")
        }
    }
    publications {
        maven(MavenPublication) {
            from components.java
        }
    }
}

本例中是透過url = uri("$rootDir/repo")將程式碼打包到maven的本地倉庫中,如果想上傳到遠端,換成遠端連結即可。透過設定GroupId、ArtifactId、Version來保證外掛的唯一性。

  • GroupId: group = 'com.fastgo'

  • ArtifactId: plugin

  • Version: version = '1.0-test'

最終的檔案如下:


圖片描述

image.png

外掛編寫完成後,在Android Studio的右上角開啟Gradle,執行:plugin分組中的publish命令,執行完成後,會在專案根目錄下生成repo倉庫:

圖片描述

image.png


4、在專案根目錄的build.gradle中引用外掛:


buildscript {
    repositories {
        maven {
            url = uri("$rootDir/repo")
        }
       ------其他-------
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:2.3.3'
        classpath 'com.fastgo:plugin:1.0-test'
    }
}
 //透過外掛id找到外掛
 apply plugin: 'com.fastgo.plugin'

 ext.greetingFile="$buildDir/hello.txt"

5、驗證效果

mqdeMacBook-Pro:AndroidStudy mq$ ./gradlew -q writeToFile
hello world

執行writeToFile的task輸出了我們寫入的文字,再看看build目錄下是否有我們想要的檔案:


圖片描述

image.png

可以看到在build目錄下生成了hello.txt檔案並往裡面寫入了設定的文字,說明我們編寫的外掛被成功引入了。



作者:_小馬快跑_
連結:
x


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/4686/viewspace-2821528/,如需轉載,請註明出處,否則將追究法律責任。

相關文章