AS 自定義 Gradle plugin 插件 案例 [MD]


博文地址

我的GitHub 我的博客 我的微信 我的郵箱
baiqiantao baiqiantao bqt20094 baiqiantao@sina.com

目錄

自定義 Gradle plugin

結合 AndroidStudio,自定義Gradle plugin可以完成很多功能,比如:添加編譯依賴、進行Aspecj編譯、自動生成混淆配置。

項目中引入自定義 Gradle plugin 一般有三種方法:

  • 直接寫在 build.gradle 中,這種方式的缺點是無法復用插件代碼,在其他項目中還得復制一遍代碼
  • plugin 源碼放到 rootProjectDir/buildSrc/src/main/groovy 目錄下(沒用過)
  • plugin 打包后發布到 maven 倉庫, 然后項目通過依賴的形式引入

下面介紹的是第 3 種方式

完整案例

編寫插件

創建插件 module

新建一個Android工程,在這個工程里面新建一個Android Library,先起名叫 cooker-plugin 吧,我們將會用這個 library 寫 Gradle plugin

建立 plugin 的目錄結構

把這個 cooker-plugin 中除了build.gradle文件外的默認文件都刪除,然后按照下面新建文件:

  • 在新建的module中新建文件夾src,接着在src文件目錄下新建main文件夾,在main目錄下新建groovy目錄,這時候groovy文件夾會被Android識別為groovy源碼目錄。
  • 除了在main目錄下新建groovy目錄外,你還要在main目錄下新建resources目錄,同理resources目錄會被自動識別為資源文件夾。
  • 在groovy目錄下新建項目包名,就像Java包名那樣。
  • 在resources目錄下新建文件夾META-INF,META-INF文件夾下新建gradle-plugins文件夾。

這樣,就完成了gradle 插件的項目的整體搭建,之后就是小細節了。目前,項目的結構是這樣的:

聲明 plugin 信息

src/main/resources/META-INF/gradle-plugins 里聲明 plugin 信息,比如新建cooker-plugin.properties文件(文件名 cooker-plugin 是插件名稱),在其中指定插件的實現類的全路徑類名:

implementation-class=com.helen.plugin.CookerPlugin

配置 build.gradle

在 build.gradle 中聲明用 groovy 開發

apply plugin: 'groovy'

dependencies {
    compile gradleApi()
    compile localGroovy()
}

repositories {
    mavenCentral()
}

編寫插件邏輯

插件代碼放在 src/main/groovy 下,實現 plugin,其實就是實現 Plugin<Project> 接口

package com.helen.plugin

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

class CookerPlugin implements Plugin<Project> {
    @Override
    void apply(Project project) {
        println "這里實現plugin的邏輯!"
        project.task('cooker-test-task').doLast { variant ->
            println "自定義任務-doLast"
        }.doFirst { variant ->
            println "自定義任務-doFirst"
        }
    }
}

生成 plugin 插件

在 cooker-plugin 項目中,build 一下

任務完成以后,就能在build/libs下生成對應的 plugin 插件了

現在這個插件就能使用了,可以發布在本地倉庫或者 Maven 倉庫

傳遞參數

接下來我們介紹如何獲得自定義的參數

定義參數

新建 PluginExtension.groovy,用於定義我們可以支持的參數:

class PluginExtension {
    def param1 = "param1 defaut"
    def param2 = "param2 defaut"
    def param3 = "param3 defaut"
}

我們希望能傳入嵌套的參數,所以再新建一個 PluginNestExtension.groovy:

class PluginNestExtension {
    def nestParam1 = "nestParam1 defaut"
    def nestParam2 = "nestParam2 defaut"
    def nestParam3 = "nestParam3 defaut"
}

DefaultTask

新建一個 CustomTask.groovy,繼承 DefaultTask 類,使用 @TaskAction 注解標注實現的方法:

class CustomTask extends DefaultTask {

    @TaskAction
    void output() {
        println "param1 is ${project.pluginExt.param1}"
        println "param2 is ${project.pluginExt.param2}"
        println "param3 is ${project.pluginExt.param3}"
        println "nestparam1 is ${project.pluginExt.nestExt.nestParam1}"
        println "nestparam2 is ${project.pluginExt.nestExt.nestParam2}"
        println "nestparam3 is ${project.pluginExt.nestExt.nestParam3}"
    }
}

這里我們只是做了拿到了參數,然后做最簡單的輸出操作,使用 ${project.pluginExt.param1}${project.pluginExt.nestExt.nestParam1} 等拿到使用者設置的值。

建立映射關系

在 apply 方法中建立映射關系:

project.extensions.create('pluginExt', PluginExtension)
project.pluginExt.extensions.create('nestExt', PluginNestExtension)
project.task('customTask', type: CustomTask)

定義外部參數

這里我們定義了param1,param2,nestParam1,nestParam2,而param3和nestParam3保持默認。

pluginExt {
    param1 = 'app param1'
    param2 = 'app param2'
    nestExt {
        nestParam1 = 'app nestParam1'
        nestParam2 = 'app nestParam2'
    }
}

這樣之后,在執行customTask時就會輸出使用者對自定義的參數設置的值

發布插件到倉庫

發布到倉庫的方式有很多,下面只介紹利用 mavenDeployer 插件發布在本地倉庫

1、引入 mavenDeplayer 插件

修改 cooker-plugin 的 build.gradle,添加如下內容:

apply plugin: 'maven'//添加maven plugin,用於發布我們的jar
uploadArchives {
    repositories {
        mavenDeployer {
            pom.groupId = 'com.helen.plugin'
            pom.artifactId = 'cooker-plugin'
            pom.version = 1.0
            repository(url: uri('../release'))   //文件發布目錄(相對當前 build.gradle 的路徑)
        }
    }
}

2、用 uploadArchices 發布

運行 uploadArchives 就能在設置的倉庫路徑中生成 cooker-plugin 了

使用插件

引入 cooker-plugin

在 build.gradle 中引入 cooker-plugin

buildscript {
    repositories {
        jcenter()
        maven {
            url uri('release') //cooker-plugin 所在的倉庫,這里是本地目錄(相對當前 build.gradle 的路徑)
        }
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:3.2.1'
        classpath 'com.helen.plugin:cooker-plugin:1.0'  //引入cooker-plugin
    }
}
apply plugin: 'cooker-plugin'

編譯

我們編譯 App 的時候 cooker-plugin 就會介入了

每次 clean/build 時, 在 Gradle Console 可以看到我們的 log

這里實現plugin的邏輯!

運行 task

前面demo中,我們新建了兩個task: cooker-test-taskcustomTask, 我們可以通過兩種方式運行這兩個task,一種方式是雙擊 app > other > task 名,一種是通過 gradlew 命令

cooker-test-task 打印的日志:

Executing tasks: [cooker-test-task]

這里實現plugin的邏輯!
:app:cooker-test-task
自定義任務-doFirst
自定義任務-doLast

customTask 打印的日志:

Executing tasks: [customTask]

這里實現plugin的邏輯!
:app:customTask
param1 is app param1
param2 is app param2
param3 is param3 defaut
nestparam1 is app nestParam1
nestparam2 is app nestParam2
nestparam3 is nestParam3 defaut

到此為止, 自定義Gradle plugin的基本過程就介紹完了。

一些細節

參考:如何使用Android Studio開發Gradle插件

前段時間,插件化以及熱修復的技術很熱,Nuwa熱修復的工具NuwaGradle,攜程動態加載技術DynamicAPK,還有希望做最輕巧的插件化框架的Small。這三個App有一個共同的地方就是大量的使用了Gradle這個強大的構建工具,除了攜程的框架外,另外兩個都發布了獨立的Gradle插件提供自動化構建插件,或者生成熱修復的補丁。所以學習一下Gradle插件的編寫還是一件十分有意義的事。

Gradle插件是使用Groovy進行開發的,而Groovy其實是可以兼容Java的。Android Studio其實除了開發Android App外,完全可以勝任開發Gradle插件這一工作。

這樣就完成了最簡單的一個gradle插件,里面有一個叫testTask的Task,執行該task后會輸出一段文字,就像當初我們輸出HelloWorld一樣。

發布到本地倉庫

接着,我們需要將插件發布到maven中央倉庫,我們將插件發布到本地倉庫就好了,在module項目下的buidl.gradle文件中加入發布的代碼。

repositories {
    mavenCentral()
}
group='cn.edu.zafu.gradle.plugin'
version='1.0.0'

uploadArchives {
    repositories {
        mavenDeployer {
            repository(url: uri('../repo'))
        }
    }
}

maven本地倉庫的目錄就是當前項目目錄下的repo目錄。這時候,右側的gradle Toolbar就會在module下多出一個名為uploadArchives的task,點擊這個Task,就會在項目下多出一個repo目錄,里面存着這個gradle插件。

使用插件

發布到本地maven倉庫后,我們就使用它。在叫app的android項目下的gradle.build的文件中加入

buildscript {
    repositories {
        maven {
            url uri('../repo')
        }
    }
    dependencies {
        classpath 'cn.edu.zafu.gradle.plugin:time:1.0.0'
    }
}
apply plugin: 'plugin.test'

同步一下gradle,右側app下other分類下就會多出一個testTask,雙擊執行這個Task,控制台就會輸出剛才我們輸入的字符串。

發布到Jcenter倉庫

配置完以后,右側的gradle的toolbar就會多出幾個task。

之后我們先運行other下的install這個task,再執行bintrayUpload這個task,如果不出意外,就上傳了,之后不要忘記到后台add to jcenter。成功add到jcenter之后就會有link to jcenter的字樣。

耐心等待add to center成功的消息,之后就可以直接引用了。

最佳實踐

最佳實踐的來源是源自multidex,為什么呢,因為最近當方法數超了之后,如果選擇multidex,編譯的過程就會慢很多很多,為了檢測到底是哪一步的耗時,需要編寫一個插件來統計各個task執行的時間,因此就有了這么一個最佳實踐。

在PluginImpl同級目錄下新建TimeListener.groovy文件:

class TimeListener implements TaskExecutionListener, BuildListener {
    //...
}

然后將PluginImpl文件中的apply方法修改為

void apply(Project project) {
   project.gradle.addListener(new TimeListener())
}

完成后打包發布到jcenter(),之后你只要引用了該插件,就會統計各個task執行的時間。

2020-04-08


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM