Skip to content

约定插件 (Convention Plugins)

源:Gradle 官方文档 - Sharing Build Logic

约定插件是现代 Gradle 推荐的构建逻辑复用方案,解决多模块项目中重复配置的问题。

为什么需要约定插件

传统方式的问题

重复配置

kotlin
// module-a/build.gradle.kts android {  compileSdk = 34  defaultConfig {  minSdk = 24  }  compileOptions {  sourceCompatibility = JavaVersion.VERSION_17  targetCompatibility = JavaVersion.VERSION_17  } }  // module-b/build.gradle.kts  android {  compileSdk = 34 // 重复!  defaultConfig {  minSdk = 24 // 重复!  }  compileOptions {  sourceCompatibility = JavaVersion.VERSION_17 // 重复!  targetCompatibility = JavaVersion.VERSION_17 // 重复!  } }

allprojects/subprojects 的问题

kotlin
// ❌ 不推荐 allprojects {  // 所有模块都受影响,包括不需要的 }  subprojects {  // 难以维护,强耦合 }

问题

  • 全局配置污染
  • 难以按需应用
  • 破坏构建缓存
  • 类型安全缺失

约定插件的优势

kotlin
// ✅ 推荐:约定插件 plugins {  id("my.android.library") // 一行搞定 }

优势

  • 按需应用
  • 类型安全
  • 代码复用
  • 独立缓存

约定插件概念

什么是约定插件

约定插件:将通用的构建配置封装成插件,各模块按需引用。

核心思想

  1. 定义"约定"(Convention)
  2. 封装成插件
  3. 模块按需应用

与普通插件的区别

特性普通插件约定插件
用途通用功能项目特定配置
发布Maven/Plugin Portal项目内部
示例Android Gradle Plugin项目的 Library 配置

buildSrc vs 约定插件

buildSrc 方案

结构

project/ ├── buildSrc/ │ ├── build.gradle.kts │ └── src/main/kotlin/ │ └── AndroidLibraryPlugin.kt ├── app/ └── core/

优点

  • 简单直接
  • 自动识别

缺点

  • ❌ 修改 buildSrc 导致全项目缓存失效
  • ❌ 难以跨项目共享
  • ❌ 构建性能差

约定插件方案(推荐)

结构

project/ ├── build-logic/ │ ├── convention/ │ │ ├── build.gradle.kts │ │ └── src/main/kotlin/ │ │ └── AndroidLibraryConventionPlugin.kt │ └── settings.gradle.kts ├── app/ └── core/

优点

  • ✅ 独立构建缓存
  • ✅ 可跨项目共享
  • ✅ 更好的隔离
  • ✅ IDE 支持更好

创建约定插件

第一步:创建 build-logic 项目

项目结构

build-logic/ ├── convention/ │ ├── build.gradle.kts │ └── src/main/kotlin/ │ └── AndroidLibraryConventionPlugin.kt └── settings.gradle.kts

build-logic/settings.gradle.kts

kotlin
dependencyResolutionManagement {  repositories {  google()  mavenCentral()  }  versionCatalogs {  create("libs") {  from(files("../gradle/libs.versions.toml"))  }  } }  rootProject.name = "build-logic" include(":convention")

build-logic/convention/build.gradle.kts

kotlin
plugins {  `kotlin-dsl` }  dependencies {  compileOnly(libs.android.gradlePlugin)  compileOnly(libs.kotlin.gradlePlugin) }

第二步:编写插件

AndroidLibraryConventionPlugin.kt

kotlin
import com.android.build.gradle.LibraryExtension import org.gradle.api.Plugin import org.gradle.api.Project import org.gradle.kotlin.dsl.configure  class AndroidLibraryConventionPlugin : Plugin<Project> {  override fun apply(target: Project) {  with(target) {  // 应用必要的插件  with(pluginManager) {  apply("com.android.library")  apply("org.jetbrains.kotlin.android")  }    // 配置 Android  extensions.configure<LibraryExtension> {  compileSdk = 34    defaultConfig {  minSdk = 24  }    compileOptions {  sourceCompatibility = JavaVersion.VERSION_17  targetCompatibility = JavaVersion.VERSION_17  }  }  }  } }

第三步:注册插件

build-logic/convention/build.gradle.kts

kotlin
gradlePlugin {  plugins {  register("androidLibrary") {  id = "my.android.library"  implementationClass = "AndroidLibraryConventionPlugin"  }  } }

第四步:引入 build-logic

主项目 settings.gradle.kts

kotlin
pluginManagement {  includeBuild("build-logic") }  dependencyResolutionManagement {  repositories {  google()  mavenCentral()  } }  rootProject.name = "MyApp" include(":app") include(":core")

第五步:使用插件

core/build.gradle.kts

kotlin
plugins {  id("my.android.library") // 使用约定插件 }  dependencies {  // 只写业务特定的依赖  implementation(libs.androidx.core.ktx) }

访问 Version Catalog

在插件中读取 libs

扩展函数

kotlin
import org.gradle.api.Project import org.gradle.api.artifacts.VersionCatalog import org.gradle.api.artifacts.VersionCatalogsExtension import org.gradle.kotlin.dsl.getByType  internal val Project.libs  get(): VersionCatalog =   extensions.getByType<VersionCatalogsExtension>().named("libs")

使用

kotlin
class AndroidLibraryConventionPlugin : Plugin<Project> {  override fun apply(target: Project) {  with(target) {  extensions.configure<LibraryExtension> {  val compileSdkVersion = libs.findVersion("compileSdk")  .get().requiredVersion.toInt()  compileSdk = compileSdkVersion  }  }  } }

多种约定插件

Android Library 插件

kotlin
// AndroidLibraryConventionPlugin.kt class AndroidLibraryConventionPlugin : Plugin<Project> {  override fun apply(target: Project) {  with(target) {  with(pluginManager) {  apply("com.android.library")  apply("org.jetbrains.kotlin.android")  }    extensions.configure<LibraryExtension> {  configureKotlinAndroid(this)  }  }  } }

Android Application 插件

kotlin
// AndroidApplicationConventionPlugin.kt class AndroidApplicationConventionPlugin : Plugin<Project> {  override fun apply(target: Project) {  with(target) {  with(pluginManager) {  apply("com.android.application")  apply("org.jetbrains.kotlin.android")  }    extensions.configure<ApplicationExtension> {  configureKotlinAndroid(this)    defaultConfig.targetSdk = 34  }  }  } }

Compose 插件

kotlin
// AndroidComposeConventionPlugin.kt class AndroidComposeConventionPlugin : Plugin<Project> {  override fun apply(target: Project) {  with(target) {  extensions.configure<CommonExtension<*, *, *, *, *, *>> {  buildFeatures {  compose = true  }    composeOptions {  kotlinCompilerExtensionVersion = libs  .findVersion("androidxComposeCompiler")  .get().requiredVersion  }    dependencies {  val bom = libs.findLibrary("androidx-compose-bom").get()  add("implementation", platform(bom))  add("implementation", libs.findLibrary("androidx-compose-ui").get())  add("implementation", libs.findLibrary("androidx-compose-material3").get())  }  }  }  } }

提取通用配置

Kotlin Android 配置

kotlin
// KotlinAndroid.kt import com.android.build.api.dsl.CommonExtension import org.gradle.api.JavaVersion import org.gradle.api.Project import org.gradle.kotlin.dsl.withType import org.jetbrains.kotlin.gradle.tasks.KotlinCompile  internal fun Project.configureKotlinAndroid(  commonExtension: CommonExtension<*, *, *, *, *, *> ) {  commonExtension.apply {  compileSdk = 34    defaultConfig {  minSdk = 24  }    compileOptions {  sourceCompatibility = JavaVersion.VERSION_17  targetCompatibility = JavaVersion.VERSION_17  }  }    // Kotlin 编译选项  tasks.withType<KotlinCompile>().configureEach {  kotlinOptions {  jvmTarget = JavaVersion.VERSION_17.toString()  }  } }

实战案例

案例1:完整的插件体系

插件列表

kotlin
// build-logic/convention/build.gradle.kts gradlePlugin {  plugins {  register("androidApplication") {  id = "my.android.application"  implementationClass = "AndroidApplicationConventionPlugin"  }  register("androidLibrary") {  id = "my.android.library"  implementationClass = "AndroidLibraryConventionPlugin"  }  register("androidCompose") {  id = "my.android.compose"  implementationClass = "AndroidComposeConventionPlugin"  }  register("androidHilt") {  id = "my.android.hilt"  implementationClass = "AndroidHiltConventionPlugin"  }  } }

使用

kotlin
// app/build.gradle.kts plugins {  id("my.android.application")  id("my.android.compose")  id("my.android.hilt") }  // feature-login/build.gradle.kts plugins {  id("my.android.library")  id("my.android.compose") }  // core/build.gradle.kts plugins {  id("my.android.library") }

案例2:Now in Android 模式

参考 Google 官方项目

build-logic/ ├── convention/ │ └── src/main/kotlin/ │ ├── AndroidApplicationConventionPlugin.kt │ ├── AndroidLibraryConventionPlugin.kt │ ├── AndroidFeatureConventionPlugin.kt │ ├── AndroidComposeConventionPlugin.kt │ ├── AndroidHiltConventionPlugin.kt │ ├── AndroidRoomConventionPlugin.kt │ └── KotlinAndroid.kt

最佳实践

插件命名

kotlin
id = "my.android.library" // 使用项目前缀 id = "my.android.compose" id = "my.android.hilt"

提取通用逻辑

kotlin
// 创建 KotlinAndroid.kt、AndroidCompose.kt 等 configureKotlinAndroid(commonExtension) configureAndroidCompose(commonExtension)

Version Catalog 访问

kotlin
internal val Project.libs  get() = extensions.getByType<VersionCatalogsExtension>().named("libs")

插件组合

kotlin
plugins {  id("my.android.library")  id("my.android.compose") // 可组合使用 }

文件组织

convention/src/main/kotlin/ ├── AndroidApplicationConventionPlugin.kt ├── AndroidLibraryConventionPlugin.kt ├── AndroidComposeConventionPlugin.kt └── utils/  ├── KotlinAndroid.kt  ├── AndroidCompose.kt  └── ProjectExtensions.kt