前言
在上一篇笔记中 Android Gradle 学习 - 生命周期和Task ,汇总了Task的基础知识,这一篇就汇总下 项目中对于插件的使用
Plugin 插件作用
Gradle Plugin 是一种用于扩展和定制 Gradle构建系统功能的机制。插件可以执行各种任务,包括编译代码、执行测试、打包文件、生成文档等等操作。插件可以访问和操作 Gradle的构建模型,如项目、任务、依赖关系等,实现对构建过程的控制和定制,从而提高开发效率。
站在工程和团队角度看 Plugin ,有三大优点:
- 封装: 把具体逻辑抽出去,降低build.gradle的复杂度。
- 复用和统一:通用逻辑可以提供给多个项目使用,统一构建规范,提供标准化配置。
- 定制拓展: 可在编译期间插桩、Hook、字节码操作等定制操作。
Plugin 不同创建方式
根据插件不同的作用域,可以细分为三种 Plugin写法
| 位置 | 作用域 | 适用场景 |
|---|---|---|
build.gradle |
当前Project | 简单逻辑、快速验证 |
buildSrc |
当前项目所有Module | 项目内共享逻辑 |
| 独立项目 | 所有项目(发布后) | 跨项目复用、开源分享 |
项目结构示意:
less
root/
├── build.gradle //方式1:写在这里
├── buildSrc/ //方式2:写在这里
│ ├── build.gradle.kts
│ └── src/main/kotlin/
│ └── MyPlugin.kt
├── my-plugin/ //方式3:独立模块
│ ├── build.gradle.kts
│ └── src/main/kotlin/
│ └── MyPlugin.kt
└── app/
└── build.gradle
build.gradle中编写插件
只在当前文件域中生效,虽然写法简单,但是不能复用,简单测试逻辑还是可以的,这里简单列举下模版代码。
kotlin
// 定义插件类
class HelloPlugin : Plugin {
override fun apply(project: Project) {
println("Hello Plugin被应用了!")
}
}
// 应用插件
apply()
Sync后输出:
markdown
> Configure project :app
Hello Plugin被应用了!
buildSrc中编写插件
buildSrc是Gradle的约定目录,放在项目根目录下:
- Gradle会自动编译这个目录
- 编译结果会自动添加到构建脚本的classpath
- 对项目中所有Module可见
less
root/
├── buildSrc/
│ ├── build.gradle.kts // 构建配置
│ └── src/
│ └── main/
│ ├── kotlin/
│ │ └── com/
│ │ └── example/
│ │ └── MyPlugin.kt // 插件
│ └── resources/
│ └── META-INF/
│ └── gradle-plugins/
│ └── my-plugin.properties //插件ID映射
├── app/
│ └── build.gradle.kts
└── settings.gradle.kts

buildSrc中的build.gradle.kts
kotlin
plugins {
`kotlin-dsl`
}
repositories {
mavenCentral()
google()
}
//如果根布局 build.gradle 中已经依赖了 agp ,这里不需要添加
buildSrc/src 文件下的 MyPlugin.kt
kotlin
class MyPlugin : Plugin {
override fun apply(project: Project) {
println("MyPlugin 应用到: ${project.name}")
// 创建扩展
val extension = project.extensions.create(
"myConfig",
MyPluginExtension::class.java
)
val taskProvider = project.tasks.register("myTask")
taskProvider.configure(object : Action {
override fun execute(task: Task) {
task.group = "my-plugin"
task.description = "MyPlugin提供的任务"
task.doLast(object : Action {
override fun execute(t: Task) {
println("执行myTask")
println("配置信息: ${extension.message.get()}")
}
})
}
})
// 项目评估完成后执行
project.afterEvaluate(object : Action {
override fun execute(p: Project) {
println("项目 ${p.name} 配置完成")
}
})
}
}
// 扩展类
abstract class MyPluginExtension {
abstract val message: Property
init {
message.convention("默认消息")
}
}
my-plugin.properties 插件id映射文件 这样可以使用简短的插件id
properties
implementation-class=MyPlugin
使用插件,这里我是放在了 build.gradle 文件中了 (根布局我是用的 groovy格式)
groovy
apply plugin: MyPlugin
// 配置扩展
myConfig {
message = "Hello from app module!"
}
独立模块编写 Plugin
这里先创建一个 Library ,然后我们再将部分配置修改为 Gradle插件

将Library 改造为 插件Module (移除 AndroidManifest.xml和proguard-rules 等混淆文件 修改build.gradle.kts)

创建 MyGradlePlugin.kt
kotlin
class MyGradlePlugin : Plugin {
companion object {
const val TAG = ">>>>>>> MyGradlePlugin: "
}
override fun apply(project: Project) {
println("$TAG 插件已应用到 ${project.name}")
// 1. 创建扩展配置
val extension = project.extensions.create(
"myPluginConfig",
MyExtension::class.java
)
// 2. 创建 Task
val taskProvider = project.tasks.register("printProjectInfo")
taskProvider.configure(object : Action {
override fun execute(task: Task) {
task.group = "my-plugin"
task.description = "打印项目信息"
task.doLast(object : Action {
override fun execute(t: Task) {
println("项目名称: ${project.name}")
println("项目路径: ${project.projectDir}")
println("应用名称: ${extension.appName.getOrElse("未设置")}")
println("版本号: ${extension.versionName.getOrElse("未设置")}")
}
})
}
})
// 3. 项目评估完成后执行
project.afterEvaluate(object : Action {
override fun execute(p: Project) {
if (extension.enable.getOrElse(true)) {
println("$TAG 插件功能已启用")
} else {
println("$TAG 插件功能已禁用")
}
}
})
}
}
修改 build.gradle.kts
kotlin
plugins {
`kotlin-dsl`
`maven-publish`
`java-gradle-plugin`
}
group = "com.xmly"
version = "1.0.0"
dependencies {
implementation(gradleApi())
}
// 配置插件信息
gradlePlugin {
plugins {
create("myPlugin") {
id = "com.xmly.myplugin" // 插件ID
implementationClass = "com.xmly.plugin.MyGradlePlugin" // 实现类
displayName = "My Gradle Plugin"
description = "示例 Gradle 插件"
}
}
}
// 发布配置
publishing {
repositories {
maven {
name = "local"
url = uri("${rootProject.projectDir}/repo") // 发布到项目根目录的 repo 文件夹
}
}
}
这里暂时选本地,就是看看效果,如果有云端 maven地址也可以换一下,然后我们进行打包,因为Library 的插件是不能直接通过 apply 依赖到
发布插件到本地
ruby
./gradlew :gradle-plugin:publish

可以查看根目录是否产生repo文件和对应版本jar 判断是否发布成功
在根目录的 setting.gradle 文件中配置 本地地址
gradle
pluginManagement {
repositories {
maven { url './repo' }
// ... 其他仓库
}
}
然后在 build.gradle.kts 中添加对应插件依赖
kotlin
plugins {
... //省略自己引入的插件
id("com.xmly.myplugin") version "1.0.0"
}
myPluginConfig {
enable.set(true)
appName.set("MyApp")
}
现在sync 我们就可以看到 MyGradlePlugin: 插件功能已启用 的字样 在命令行中执行 ./gradlew :app:printProjectInfo 即可看到
gradle
> Task :app:printProjectInfo
[执行] 开始::app:printProjectInfo
项目名称: app
项目路径: /Users/mingchuan.yang/Downloads/ASWorker/GradleY/app
应用名称: GradleY App
版本号: 2.0.0
成功 [执行] 完成::app:printProjectInfo
Gradle 构建成功
Extension 插件扩展
Extension 示例
xtension是插件提供的配置入口 ,有点类似于Android插件的android { }闭包。
kotlin
android {
compileSdk = 34
defaultConfig {
minSdk = 24
targetSdk = 34
}
}
// 自定义插件的Extension
myPluginConfig {
enable = true
appName = "MyApp"
}
我们上边之所以 可以使用 myPluginConfig {} 来设置部分你参数,就是实现了 一个 MyExtension.kt
kotlin
abstract class MyExtension {
//是否启用插件
abstract val enable: Property
//应用名称
abstract val appName: Property
//版本号
abstract val versionName: Property
init {
enable.convention(true)
appName.convention("MyApp")
versionName.convention("1.0.0")
}
}
Extension 嵌套扩展
我们先创建一个 嵌套的扩展类
kotlin
abstract class OuterExtension(project: Project) {
abstract val name: Property
// 嵌套的Extension
val inner: InnerExtension = project.objects.newInstance(InnerExtension::class.java)
// DSL方法,支持闭包配置
fun inner(action: Action) {
action.execute(inner)
}
}
// 内层Extension
abstract class InnerExtension {
abstract val enabled: Property
abstract val debug: Property
init {
enabled.convention(true)
debug.convention(false)
}
}
定义扩展测试用的 Plugin
kotlin
class NestedPlugin : Plugin {
override fun apply(project: Project) {
val extension = project.extensions.create(
"nested",
OuterExtension::class.java,
project // 传递project参数
)
project.afterEvaluate {
println("Name: ${extension.name.get()}")
println("Enabled: ${extension.inner.enabled.get()}")
println("Debug: ${extension.inner.debug.get()}")
}
}
}
在 gradle-plugin library下的 build.gradle.kts 对新的插件进行注册
kotlin
gradlePlugin {
plugins {
// 普通插件示范注册省略了这里
...
// 嵌套扩展示例插件
create("nestedPlugin") {
id = "com.xmly.nested"
implementationClass = "com.xmly.plugin.NestedPlugin"
displayName = "Nested Extension Plugin"
description = "嵌套扩展插件"
}
}
}
在app build.gradle 中进行注册和plugin设置
kotlin
plugins {
alias(libs.plugins.android.application)
alias(libs.plugins.kotlin.android)
alias(libs.plugins.kotlin.compose)
id 'com.xmly.myplugin' version '1.0.0' // 使用我们发布的插件
id 'com.xmly.nested' version '1.0.0'
}
...
// 嵌套扩展入口
nested {
name.set("我的配置")
inner {
enabled.set(true)
debug.set(true)
}
}
以上示例中的发布都是发布到本地,实际工作中都是 maven地址,大概需要添加的配置包括,maven远端地址 ,签名和 pom等配置,由于公司里的数据,这里我暂时给一个示例
gradle-plugin 文件夹下的 build.gradle.kts
kotlin
plugins {
...
signing // 发布用的
}
group = "com.xmly"
version = "1.0.0"
java {
withJavadocJar()
withSourcesJar()
}
dependencies {
implementation(gradleApi())
}
publishing {
publications {
create("maven") {
from(components["java"])
pom {
name.set("xxxx")
//换成你自己的 maven地址
url.set("https://xxx")
licenses {
license {
name.set("Apache License 2.0")
url.set("https://www.apache.org/licenses/LICENSE-2.0")
}
}
developers {
developer {
// 这里写自己的开发信息就好
id.set("example")
name.set("Example Developer")
email.set("dev@example.com")
}
}
scm {
// 开源仓库信息
connection.set("scm:git:git://xxxx.git")
developerConnection.set("scm:git:ssh://xxxx.git")
url.set("https://xxxxx/my-plugin")
}
}
}
}
repositories {
maven {
name = "OSSRH"
url = uri("https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/")
credentials {
// 默认都放在 gradle.properties 文件中,不要硬编码 个人使用自己的信息
username = project.findProperty("ossrhUsername") as String?
password = project.findProperty("ossrhPassword") as String?
}
}
}
}
// 配置签名
signing {
sign(publishing.publications["maven"])
}
一般问 ld 要maven地址即可 ,自己替换申请到的 用户名和密码即可
最后
这里主要讲本地的 plugin 编写和发布逻辑 总结了一下,远端这里每个公司 应该都有自己的一套 publishMaven 逻辑,所以 不过多编写了, 后边还有 插桩和字节码操作等笔记,不过比较乱,可能输出时间到不固定
参考文档
本文借鉴了很多大佬优秀的文章,感谢下面的文章作者