3. settings.gradle 多模块仓库声明

settings.gradle 多模块仓库声明 学习卡片

为什么多模块工程必须先解决“入口”问题

单模块工程里,仓库地址和插件版本可以随手写在根 build.gradle 里。一旦拆成多模块,每个子模块都会独立解析插件和依赖,如果入口不统一,就会出现:子模块找不到插件、仓库重复声明、CI 与本地解析结果不一致。Gradle 7 之后,settings.gradle 成为集中管理这些入口的推荐位置,它比根构建脚本更早执行,能决定“去哪里找插件、去哪里找依赖、有哪些模块参与构建”。

settings.gradle 的三块核心声明

一个面向多模块 Android 工程的 settings.gradle 通常由三部分组成,它们分别解决三个不同层面的问题:

  • pluginManagement:声明插件从哪些仓库解析,以及插件版本如何统一。它只影响 plugins {} 块中的插件解析。
  • dependencyResolutionManagement:声明普通依赖(implementation、api 等)从哪些仓库解析,并可通过 repositoriesMode 禁止子模块自行声明仓库。
  • include:声明哪些目录是参与构建的模块,决定 Gradle 的项目树结构。
pluginManagement 与 dependencyResolutionManagement 是两套独立的仓库入口:前者管插件,后者管依赖。把插件仓库写进 dependencyResolutionManagement,或把依赖仓库写进 pluginManagement,都会导致解析失败。

完整示例:一个可运行的多模块 settings.gradle

下面是一个包含 app、core、feature-login 三个模块的工程入口配置。仓库使用 google()、mavenCentral() 与 gradlePluginPortal(),并强制子模块不得自行声明仓库。

// settings.gradle
pluginManagement {
    repositories {
        google {
            content {
                includeGroupByRegex("com\\.android.*")
                includeGroupByRegex("com\\.google.*")
                includeGroupByRegex("androidx.*")
            }
        }
        mavenCentral()
        gradlePluginPortal()
    }
}

dependencyResolutionManagement {
    repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
    repositories {
        google()
        mavenCentral()
    }
}

rootProject.name = "MultiModuleDemo"

include(":app")
include(":core")
include(":feature-login")

关键点说明:

  • repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) 会让任何子模块中出现的 repositories {} 直接报错,从机制上保证仓库入口唯一。
  • google() 中的 content { includeGroupByRegex(...) } 是可选优化,用于限制该仓库只处理 Android/Google 相关坐标,减少无效网络请求。
  • include(":feature-login") 中的冒号前缀表示从根项目出发的路径,目录名与模块名一致时可直接使用。

模块目录与 include 路径的对应关系

如果模块目录不是平铺在根目录下,而是放在 features/ 子目录中,include 路径必须反映真实目录层级,同时可通过 project().projectDir 重定向目录。下图展示了 settings.gradle 如何把逻辑模块名映射到物理目录,以及插件与依赖两条解析链的入口关系。

settings.gradle 构建入口 · 最先执行 pluginManagement 插件解析入口 google() / mavenCentral() gradlePluginPortal() dependencyResolution Management 依赖解析入口 google() / mavenCentral() FAIL_ON_PROJECT_REPOS include 模块树声明 :app :core :feature-login 物理目录结构 app/ build.gradle core/ build.gradle features/login/ build.gradle settings.gradle 根目录入口文件 include(":feature-login") → features/login/ 插件解析走 pluginManagement,依赖解析走 dependencyResolutionManagement,模块树由 include 决定

验证配置是否生效

配置完成后,用以下命令验证模块树和仓库入口是否符合预期:

# 查看参与构建的模块列表
./gradlew projects

# 查看依赖解析使用的仓库
./gradlew :app:dependencies --configuration debugRuntimeClasspath

# 若子模块误写了 repositories,会直接报错
./gradlew :core:assembleDebug

预期结果:projects 输出中应包含 :app、:core、:feature-login 三个项目;dependencies 任务不应出现“repository not found”类错误;若在 core/build.gradle 中临时加入 repositories { mavenCentral() },构建应因 FAIL_ON_PROJECT_REPOS 而失败,移除后恢复正常。

如果模块目录与模块名不一致,例如目录为 features/login 但希望模块名为 :feature-login,可写成:
include(":feature-login")
project(":feature-login").projectDir = file("features/login")
不要把 pluginManagement 块写在 dependencyResolutionManagement 之后或嵌套其中,两者必须是 settings.gradle 的顶层块。另外,pluginManagement 必须出现在文件最前面,否则 Gradle 会报解析顺序错误。