文章目錄
- 1. Gradle插件是什么
- 2. 為什么需要插件
- 3. 編寫插件位置
- 4. 編寫插件
- 5. 自定義插件擴展
- 5.1 訂閱擴展對象
- 5.2 把擴展添加給Plugin并使用
- 5.3 配置參數
- 5.4 嵌套擴展
- 5.4.1 定義擴展
- 5.4.2 獲取擴展屬性
- 5.4.3 使用
- 5.4.4 執行
- 5.4.5 輸出
- 6. 編寫在單獨項目里
- 6.1 新建Module
- 6.2 新建文件添加依賴
- 6.2.1 添加依賴
- 6.2.2 新建類
- 6.3 本地發布
- 6.3.1 Maven插件
- 6.3.2 發布配置
- 6.3.3 執行發布操作
- 6.4 使用
- 6.5 具體功能實現
1. Gradle插件是什么
Gradle插件(Plugin)是一種用于擴展和定制Gradle構建系統功能的機制。Gradle插件可以執行各種任務,包括編譯代碼、執行測試、打包文件、生成文檔等等。插件可以訪問和操作Gradle的構建模型,如項目、任務、依賴關系等等,從而實現對構建過程的控制和定制。
許多流行的工具和框架,例如Kotlin,Spring Boot,Android都有相應的Gradle插件。
比如熟悉的Android插件com.android.application
plugins {id 'com.android.application'
}
2. 為什么需要插件
- 定制:如果需要在編譯期做一些插樁,Hook之類的自定義操作,也需要用到編譯插件
- 封裝:把具體的邏輯抽出去,只需運行插件,不需要寫在某個build.gradle文件中
- 復用:把通用的邏輯抽離出去,需要用的時候apply應用插件即可,不需要一遍遍的復制,也可以提供給別的項目使用
3. 編寫插件位置
- 寫在build.gradle文件中,作用域當前project
- 寫在buildSrc里,作用域為當前項目所有
- 寫在單獨項目里,發布后可提供給所有項目
4. 編寫插件
編寫一個插件Plugin,只需要實現Plugin接口,并實現唯一apply方法即可。
在build.gradle文件中
class YiRanPlugin implements Plugin<Project>{@Overridevoid apply(Project target) {println("this is plugin ${this.class.name}")}
}apply plugin: YiRanPlugin
apply方法是調用的PluginAware接口的apply()方法,參數是一個map,用來映射Plugin id
點擊sync,輸出結果
Task是Project里的方法,我們可以通過Project取創建一個Task,apply方法中提供了Project對象,可以通過Plugin去創建一個Task
class YiRanPlugin implements Plugin<Project>{@Overridevoid apply(Project target) {println("this is plugin ${this.class.name}")target.task("YiRanPluginTask"){task ->task.doLast{println("this is Plugin ${this.class.name},create a task ${task.name}")}}}
}apply plugin: YiRanPlugin
我們在Plugin里創建了一個Task,這時候sync是不會執行Task里面打印的東西,因為是doLast在 task 執?過程中被執?,發?在 execution 階段。因此需要單獨執行
./gradlew YiRanPluginTask
輸出
> Configure project :app
this is plugin YiRanPlugin> Task :app:YiRanPluginTask
this is Plugin YiRanPlugin,create a task YiRanPluginTask
當我們依賴YiRanPlugin插件的時候,這個apply就會把插件放到PluginContainer里,同時這個apply也是在編譯階段執行Plugin接口的apply()方法,所有sync同步后構建會有輸出。
5. 自定義插件擴展
自定義插件的時候經常會有這種自定義配置的需求,通過自定義的配置可以讓我們的插件提供更豐富的能力。這些配置就是通過擴展插件來的。
5.1 訂閱擴展對象
可以是一個接口,也可以是一個類
interface YiRanPluginExtension{Property<String> getTitle()
}
5.2 把擴展添加給Plugin并使用
class YiRanPlugin implements Plugin<Project>{@Overridevoid apply(Project target) {println("this is plugin ${this.class.name}")def extension = target.extensions.create("YiRan",YiRanPluginExtension)target.task("YiRanPluginTask"){task ->task.doLast{println("this is Plugin ${this.class.name},create a task ${task.name}")println("-------")println(extension.title.get())}}}
}
target.extensions.create方法接收兩個參數:
- 1.名字,比如android,YiRan
- 2.擴展對象,然后返回這個擴展對象,通過這個擴展對象的方法可以獲取自定義的配置參數
5.3 配置參數
有兩種方式可以寫
YiRan.title = "YiRan"
YiRan{title = "YiRan"
}
如果沒有設置配置參數的話,Gradle也提供了默認值的配置
extension.title.convention("YiRan")
5.4 嵌套擴展
向我們app目錄下的build.gradle文件中,android{}里面還有defaultConfig{}
android {namespace 'com.example.gradlestudy'compileSdk 35defaultConfig {applicationId "com.example.gradlestudy"minSdk 24targetSdk 34versionCode 1versionName "1.0"testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"}
}
嵌套擴展其實就是套娃
5.4.1 定義擴展
class YiRanPluginExtension{String titleint chapterNumSubExtension subExtensionYiRanPluginExtension(Project project){subExtension = project.extensions.create("sub",SubExtension.class)}
}class SubExtension{String author
}
增加定義了一個SubExtension類,然后在YiRanPluginExtension實例化的時候加到ExtensionContainer中。
類嵌套的話,不能寫成內部類,不然編譯識別不過。
5.4.2 獲取擴展屬性
class YiRanPlugin implements Plugin<Project>{@Overridevoid apply(Project target) {println("this is Plugin:${this.class.name}")def extension = target.extensions.create("YiRan",YiRanPluginExtension)target.task("YiRanTaskPluginTask"){ task ->task.doLast{println("this is Plugin ${this.class.name},create a task ${task.name}")println("title = ${extension.title}")println("chapter = ${extension.chapterNum}")println("author = ${extension.subExtension.author}")}}}
}
可以看到和上面的例子,少了Property對象的.get()方法,如果需要的話,在類對象定義即可,例如
增加setter/getter方法
class YiRanPluginExtension{String titleint chapterNumSubExtension subExtensionYiRanPluginExtension(Project project){subExtension = project.extensions.create("sub",SubExtension.class)}void setTitle(String name){title = name}String getTitle(){return title}
}
使用的時候
extension.setTitle("調用get方法")
extension.getTitle()
5.4.3 使用
YiRan{title = "測試一下"chapterNum = 100sub{author = "YiRan"}
}
閉包配置中多了一個sub{}的閉包,就是在YiRanPluginExtension類中定義的
5.4.4 執行
./gradlew YiRanTaskPluginTask
5.4.5 輸出
6. 編寫在單獨項目里
上面的例子,我們的Plugin是寫在build.gradle文件中,而在實際項目中,為了復用,一般都是寫在buildSrc或者單獨項目中。
測試寫一個打印項目中所有依賴的插件
6.1 新建Module
類型選擇Library,或下面的Java or Kotlin Library
6.2 新建文件添加依賴
6.2.1 添加依賴
在plugin>build.gradle文件中依賴插件:
//java、gradleApi()依賴
apply plugin:'java-gradle-plugin'
//kotlin
apply plugin: 'org.jetbrains.kotlin.jvm'
6.2.2 新建類
新建一個DependenciesPlugin類
6.3 本地發布
6.3.1 Maven插件
在plugin>build.gradle文件中先依賴一個maven發布的插件’maven-publish’
apply plugin: 'maven-publish'dependencies {implementation 'com.android.tools.build:gradle:8.6.1'}
6.3.2 發布配置
添加發布配置
group 'com.example.plugin'
version '1.0.1'publishing {//配置Plugin GAVpublications {maven(MavenPublication){groupId = groupartifactId = 'dependencies'version = version}}//配置倉庫地址repositories {maven {url layout.buildDirectory.dir("maven-repo")}}
}
6.3.3 執行發布操作
./gradlew publish
build文件夾下生成有本地發布配置的maven-repo文件夾
6.4 使用
1.settings.gradle文件中配置倉庫地址
pluginManagement {repositories {// ...maven {url './plugin/build/maven-repo'}
}
}
dependencyResolutionManagement {repositories {// ...maven {url './plugin/build/maven-repo'}}
}
libs.versions.toml中添加插件依賴
[plugins]
//-----
dependencies = { id = "com.example.plugin.dependencies", version.ref = "dependencies" }
app目錄build.gradle引入
plugins {
//------alias(libs.plugins.dependencies)
}
本地依賴使用的時候,要先發布,再依賴插件,否則就會出現cannot found找不到依賴的情況。
編譯查看效果
> Configure project :app
---> com.example.plugin.DependenciesPlugin
能夠正確打印
6.5 具體功能實現
上面只是一個打印,繼續實現我們的功能,把所有的依賴打印出來。
打印依賴的方式有很多,比如可以使用Gradle命令
./gradlew app:dependencies
改造Plugin
package com.example.pluginimport com.android.build.gradle.AppExtension
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.api.artifacts.Configuration
import org.gradle.api.artifacts.Dependency
import org.gradle.api.artifacts.ModuleVersionIdentifierclass DependenciesPlugin:Plugin<Project> {companion object{const val TAG = "DependenciesPlugin >>>>>"}override fun apply(target: Project) {println("---> ${this.javaClass.name}")//獲取擴展對象val extension:DependenciesPluginExtension = target.extensions.create("printDependencies",DependenciesPluginExtension::class.java)//配置階段完成才能讀取參數,才能拿到所有依賴target.afterEvaluate {extension.getEnable.convention(false)if(extension.getEnable.get()){println("$TAG 已開啟依賴打印")val androidExtension:AppExtension = target.extensions.getByType(AppExtension::class.java)androidExtension.applicationVariants.all { applicationVariant->println("$TAG >>>>>> applicationVariant.getName() = ${applicationVariant.name}")val configuration:Configuration = applicationVariant.compileConfigurationval allDependencies:Set<Dependency> = configuration.allDependenciesval androidLibs = ArrayList<String>()val otherLibs = ArrayList<String>()configuration.resolvedConfiguration.lenientConfiguration.allModuleDependencies.forEach {resolvedDependency ->val identifier: ModuleVersionIdentifier = resolvedDependency.module.idif(identifier.group.contains("androidx")||identifier.group.contains("com.google")||identifier.group.contains("org.jetbrains")){androidLibs.add(identifier.group+":"+identifier.name+":"+identifier.version)}else{otherLibs.add(identifier.group+":"+identifier.name+":"+identifier.version)}}println("--------------官方庫 start--------------");androidLibs.forEach(System.out::println);println("--------------官方庫 end--------------");println("--------------三方庫 start--------------");otherLibs.forEach(System.out::println);println("--------------三方庫 end--------------");}}else{println("$TAG 已關閉依賴打印")}}}
}
project.afterEvaluate方法中去獲取擴展配置,因為apply plugin的執行時機早于擴展配置,否則獲取不到擴展配置的值
擴展:
package com.example.pluginimport org.gradle.api.provider.Propertyinterface DependenciesPluginExtension {val getEnable:Property<Boolean>}
使用:
printDependencies {setGetEnable(true)
}
編譯輸出:
通過獨立項目中定義插件,把所有依賴的區分打印出來了。