Gradle Settings API:settings.gradle.kts 详解

说实话,很多 Android 开发者对 settings.gradle.kts 的认知,还停留在「项目入口」这个层面。我以前带团队时发现,不少人甚至不知道这个文件能写逻辑代码。嗯,今天我们就把它彻底讲透。

Settings 文件的前世今生

在 Gradle 4.x 时代,settings.gradle 基本就是个摆设——放几个 include 就完事了。但从 Gradle 6.8 开始,Settings API 被大幅增强,到了 Gradle 7.0+,它已经成了整个构建系统的「控制面板」。

我个人习惯把 settings.gradle.kts 看作项目的「启动配置中心」。它比 build.gradle.kts 执行得更早,能做的事情也更多。

核心功能一:模块管理(include)

最基础的功能,就是声明子模块。但这里有个坑——我见过有人把 include 写成了字符串列表,结果编译报错半天找不到原因。

// 标准写法
include(":app")
include(":library:core")
include(":library:network")

// 也可以一行搞定
include(":app", ":library:core", ":library:network")

// 动态 include —— 这个技巧我常用
file("modules").listFiles()?.forEach { dir ->
    if (dir.isDirectory && File(dir, "build.gradle.kts").exists()) {
        include(":${dir.name}")
        project(":${dir.name}").projectDir = dir
    }
}
注意:include 的路径分隔符是冒号,不是斜杠。我当年从 Eclipse 转 Android Studio 时,在这上面栽过跟头。

核心功能二:插件管理(pluginManagement)

这是 Gradle 6.8 引入的重磅特性。以前插件版本散落在各个 build.gradle.kts 里,管理起来简直噩梦。现在统一在 settings.gradle.kts 里声明,爽多了。

pluginManagement {
    repositories {
        google()
        mavenCentral()
        gradlePluginPortal()
        // 私有仓库 —— 我在公司内部搭建过
        maven { url = uri("https://plugins.mycompany.com") }
    }

    // 版本统一声明
    plugins {
        id("com.android.application") version "8.2.0"
        id("org.jetbrains.kotlin.android") version "1.9.20"
    }

    // 版本解析策略 —— 解决冲突用的
    resolutionStrategy {
        eachPlugin {
            if (requested.id.namespace == "com.android") {
                useVersion("8.2.0")
            }
        }
    }
}

你想想看,如果每个模块都写一遍插件版本,升级时得改多少个文件?有了 pluginManagement,改一处就完事。

核心功能三:依赖版本目录(Version Catalog)

Version Catalog 是 Gradle 7.0 推出的,说白了就是给依赖版本号建个「字典」。我强烈建议所有新项目都用这个。

首先在 gradle/libs.versions.toml 里定义:

[versions]
kotlin = "1.9.20"
compose = "1.5.4"
retrofit = "2.9.0"

[libraries]
kotlin-stdlib = { module = "org.jetbrains.kotlin:kotlin-stdlib", version.ref = "kotlin" }
compose-ui = { module = "androidx.compose.ui:ui", version.ref = "compose" }
retrofit-core = { module = "com.squareup.retrofit2:retrofit", version.ref = "retrofit" }

[plugins]
android-application = { id = "com.android.application", version.ref = "agp" }
kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }

然后在 settings.gradle.kts 里启用:

dependencyResolutionManagement {
    repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
    repositories {
        google()
        mavenCentral()
    }
    versionCatalogs {
        create("libs") {
            from(files("gradle/libs.versions.toml"))
        }
    }
}
我的经验:Version Catalog 的命名建议用「libs」,这是官方惯例。团队里新成员一看就懂,不用额外解释。

使用的时候,在 build.gradle.kts 里直接引用:

dependencies {
    implementation(libs.kotlin.stdlib)
    implementation(libs.compose.ui)
    implementation(libs.retrofit.core)
}

这样做的好处是什么?版本号统一管理,升级时只改一个文件。我曾经在一个 30+ 模块的项目里,把版本升级从半天缩短到了 10 分钟。

核心功能四:includeBuild 与复合构建

includeBuild 是 Gradle 的「大招」。它允许你把另一个 Gradle 项目作为依赖引入。我常用它来引入本地开发的 SDK 或工具库。

// 引入本地构建的插件项目
includeBuild("../my-gradle-plugin")

// 引入本地开发的 SDK
includeBuild("../network-sdk") {
    dependencySubstitution {
        substitute(module("com.example:network-sdk"))
            .using(project(":network-sdk"))
    }
}

// 甚至可以引入多个
includeBuild("../shared-lib")
includeBuild("../analytics-sdk")

为什么要用 includeBuild 而不是 include?区别在于:include 是把模块拉进当前项目,includeBuild 是引入一个独立的 Gradle 构建。前者共享同一个构建生命周期,后者各自独立。

适用场景:
  • 本地开发 SDK,需要实时调试
  • 自定义 Gradle 插件,边开发边测试
  • 跨项目共享构建逻辑

知识体系总览

下面这张图,是我梳理的 Settings API 核心脉络:

settings.gradle.kts 模块管理 include / projectDir 插件管理 pluginManagement 版本目录 Version Catalog 复合构建 includeBuild 依赖解析管理 dependencyResolutionManagement 仓库配置 repositories 构建配置的「控制面板」 统一管理模块、插件、依赖、仓库

避坑指南

讲几个我踩过的坑:

  • 插件版本冲突:我曾经在 pluginManagementbuild.gradle.kts 里同时声明了同一个插件但版本不同,结果 Gradle 报错说「ambiguous」。后来统一只在 pluginManagement 里声明,问题解决。
  • Version Catalog 路径:默认路径是 gradle/libs.versions.toml,但如果你改了路径,记得在 from(files(...)) 里写对。我见过有人写成 from("libs.versions.toml"),结果死活找不到文件。
  • includeBuild 的循环依赖:两个项目互相 includeBuild,Gradle 会直接崩溃。别问我怎么知道的。

总结

settings.gradle.kts 早已不是当年的「小透明」。它承担了模块管理、插件管理、版本目录、复合构建等核心职责。我个人建议,新项目从一开始就把这些配置用好,省得后面重构。

记住一句话:能用 Settings API 解决的问题,就别往 build.gradle.kts 里塞。这样你的项目结构会更清晰,维护成本也更低。

小技巧:如果你不确定某个配置该放哪,先问自己「这个配置是所有模块共享的吗?」如果是,放 settings.gradle.kts;如果只是某个模块独有,放对应的 build.gradle.kts。

公众号:蓝海资料掘金营,微信deep3321