Gradle 和 Maven 在多模块项目管理上确实有一些区别,但它们的核心思想是相似的。在 Android 项目中,根目录的build.gradle文件和每个模块内的build.gradle文件扮演着不同的角色。
Android项目中build.gradle文件的分工
在 Android 项目里,一般有两类build.gradle文件:
1. 根目录的build.gradle** (Top-level build file)**
这个文件主要用于定义整个项目的全局配置,它相当于项目的“总指挥”。
plugins {
alias(libs.plugins.android.application) apply false
alias(libs.plugins.kotlin.android) apply false
id 'com.google.dagger.hilt.android' version '2.57' apply false
}主要职责:
- 定义插件版本: 根目录的
build.gradle文件中通常使用plugins { ... }块来声明整个项目需要使用的插件及其版本号。这些插件本身可能不直接应用到所有模块,但它们会作为“库”被模块引用。例如:alias(libs.plugins.android.application) apply falseid 'com.google.dagger.hilt.android' version '2.57' apply false
这里的apply false非常重要,它表示该插件只被声明,但不会立即应用。它仅仅是告诉Gradle,这个项目可以“使用”这些插件,而具体的应用工作交给各个模块自己决定。
- 集中管理依赖: 在一些更现代的Gradle配置中,你可能会看到根目录的
build.gradle或settings.gradle文件与版本目录(Version Catalogs)结合使用,来集中管理所有依赖的版本,避免在不同模块中重复声明。
特点:
- **作用对象:**整个项目,包括所有模块(module)。
- 定义全局版本号 / 依赖 / 插件,供子模块使用。
- 类似 Maven 的 parent pom,但不是直接“继承”,更像“声明共享的配置和插件版本”。
举例:Hilt 插件在根目录声明了版本 2.57,子模块只需要写 id("com.google.dagger.hilt.android"),就会用这个版本号。
2. 模块内的build.gradle** (Module-level build file)**
这个文件是每个模块自己的“配置文件”,它定义了该模块的具体行为。
plugins {
id("com.android.library")
alias(libs.kotlin.android)
id("kotlin-kapt")
id("com.google.dagger.hilt.android")
}
android {
namespace 'com.example.applistener_bestpractices'
compileSdk 35
...
}
dependencies {
implementation libs.androidx.core.ktx
implementation libs.androidx.appcompat
...
implementation "com.google.dagger:hilt-android:2.57"
kapt "com.google.dagger:hilt-compiler:2.57"
}主要职责:
- 应用插件: 模块内的
build.gradle会通过plugins { ... }块来真正应用它所需要的插件。这些插件必须在根目录的build.gradle中已经声明过。例如:id("com.android.library")id("com.google.dagger.hilt.android")
当一个模块应用了com.android.library插件,Gradle 就知道这是一个 Android 库模块;当它应用了com.google.dagger.hilt.android插件,Gradle 就会为它启用 Dagger Hilt 的特定功能。
- 配置模块: 在
android { ... }块中,你将配置该模块独有的信息,例如:namespace(包名)compileSdk和minSdk版本buildTypes(发布和调试配置)compileOptions等
- 声明依赖: 在
dependencies { ... }块中,你将声明该模块所需要的具体依赖库。这些依赖只对当前模块有效,不会自动传递给其他模块,除非你将这个模块作为依赖添加给其他模块。
特点:
- 作用对象:单个模块(可以是 app 模块,也可以是 library 模块)。
- 配置 Android 编译选项(compileSdk、minSdk、buildTypes 等)。
- 配置 模块依赖(implementation / api / testImplementation)。
- 引入插件(如 Hilt、Kotlin)—会自动用根目录声明的版本。
Gradle 与 Maven 的类比和区别
将根目录的build.gradle与 Maven 的父 POM 进行类比是很恰当的,但它们的工作方式有所不同。
- Maven 的父子关系: Maven 的父 POM(
pom.xml)通过<dependencyManagement>来管理所有子模块的依赖版本。一旦在父 POM 中声明了依赖,子模块可以省略版本号来直接继承。这种方式是继承和传递。父 POM 更像一个模板,子模块从中继承配置。- 父 POM 引入依赖,子模块自动继承。
- 子模块间的依赖默认不冲突,因为每个模块都有自己的依赖作用域。
- Gradle 的“应用”关系: Gradle 的根
build.gradle不是通过继承,而是通过声明和应用来工作的。根文件只是声明了项目中所有可用的“工具箱”,而每个模块需要自己决定要从这个工具箱里取出哪些工具(插件)和材料(依赖)来使用。- 根
build.gradle声明插件和版本,但不自动应用。 - 模块
build.gradle根据需要显式应用插件和声明依赖。
- 根
这种“声明与应用”的设计让 Gradle 的配置更加灵活和模块化。它避免了 Maven 中一些复杂的继承问题,使每个模块的配置文件都更加清晰和独立。
| 特性 | Maven | Gradle (Android) |
|---|---|---|
| 父子关系 | 父 POM 可以定义依赖、插件,子模块继承父模块依赖 | 根 build.gradle 可以声明插件版本和全局配置,子模块引用后生效 |
| 依赖传递 | 依赖继承(父 pom → 子模块自动继承依赖) | 默认不自动继承 dependencies,需要在根 build.gradle 用subprojects { dependencies { ... } }才能共享依赖 |
| 插件管理 | 在 POM 插件管理中统一版本 | 根 build.gradle 中用plugins { id ... version ... apply false }声明,子模块apply时使用该版本 |
| 模块互依赖 | 父模块不影响子模块之间依赖冲突 | 模块依赖冲突需要手动处理,Gradle 有implementation/api/compileOnly区分依赖可见性 |
| 灵活性 | XML,比较固定 | Groovy / Kotlin DSL,非常灵活,可用条件逻辑、循环、函数封装 |
根 build.gradle 和模块 build.gradle 的实际关系
可以这样理解:
- 插件版本管理
根 build.gradle 声明 Hilt、Kotlin、Android 插件版本,子模块只要id("...")就会引用根目录声明的版本。 - 不自动继承 dependencies
Gradle 默认 模块间依赖不会自动继承。每个模块的dependencies都是独立的。- 如果你想共享依赖,可以在根 build.gradle 写
subprojects { dependencies { ... } }或者用 平台(platform)依赖。
- 如果你想共享依赖,可以在根 build.gradle 写
- 配置继承
根目录可以配置一些全局参数,比如:
subprojects {
tasks.withType(JavaCompile) {
sourceCompatibility = JavaVersion.VERSION_21
targetCompatibility = JavaVersion.VERSION_21
}
}这样子模块就统一了编译选项。
总结
根目录的build.gradle文件是项目的全局配置中心,它决定了整个项目有哪些插件和依赖版本可用;而每个模块的build.gradle文件则是该模块的局部配置中心,它决定了该模块如何使用这些可用的工具来构建自己。
💡 提示:
- 现代 Android 推荐用 版本管理 + plugins alias + libs.versions.toml 来统一依赖版本。
- 每个模块保持独立的 dependencies,可以减少冲突,但也要主动在根 build.gradle 或 platform 管理公共库版本。
更新: 2025-09-05 09:49:44
原文: https://www.yuque.com/dongpozhouzi-mshe3/zhm85g/hgtcoaa8ofoto306