我的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-task
和customTask
, 我們可以通過兩種方式運行這兩個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