1. 多模块项目结构设计
在现代 Java/Android 工程中,随着业务规模和团队人数的增长,单模块项目逐渐暴露出编译缓慢、职责耦合、重复代码等痛点。Gradle 多模块构建通过将一个大型工程拆分为多个逻辑独立、相互协作的子项目,实现关注点分离、编译加速和复用提升。本章从结构设计出发,讲解模块划分原则、目录约定以及根项目与子项目的协作关系,为后续构建优化打下基础。
1.1 模块划分原则
合理的模块划分是多模块构建成功的基石。以下是经过大量项目验证的四个核心原则:
| 原则 | 说明 | 反例 |
|---|---|---|
| 单一职责 | 每个模块只关注一个业务/技术领域,如数据访问、业务逻辑、UI 展示等 | 一个模块同时包含数据层和网络层,导致改动相互影响 |
| 可复用性 | 通用工具、公共模型、基础库应抽取为独立模块,供其他模块依赖 | 在每个业务模块都复制一份相同的工具类 |
| 依赖方向稳定 | 上层业务模块依赖下层基础模块,不允许下层引用上层接口 | 数据层模块直接依赖了 UI 层的 View 类 |
| 可测试性 | 模块可脱离其他模块单独编译和运行测试 | 模块强依赖外部容器或数据库,无法进行单元测试 |
实战建议:在项目初期建立模块清单,用表格罗列每个模块的名称、职责、对外暴露接口和依赖关系,并在代码评审中持续维护。
1.2 目录约定
Gradle 通过settings.gradle声明参与构建的子项目,子项目的目录结构与模块名称通常一一对应。推荐采用以下约定:
root-project/
├── settings.gradle # 声明子项目
├── build.gradle # 根项目配置(可选)
├── gradle/
│ ├── wrapper/
│ └── libs.versions.toml # 版本目录
├── common/ # 公共模块
│ ├── build.gradle
│ └── src/
├── data/ # 数据层模块
│ ├── build.gradle
│ └── src/
├── core/ # 核心业务模块
│ ├── build.gradle
│ └── src/
└── web/ # Web 展示模块
├── build.gradle
└── src/
子模块名称与目录名一致,且采用小写 + 连字符(如user-service)便于阅读和命令行操作。
1.3 根项目与子项目的关系
根项目(Root Project)是整个多模块构建的入口,负责声明子项目、提供公共插件和依赖版本,一般不包含业务代码。子项目(Subproject)是实际构建单元,每个子项目有独立的build.gradle,也可通过根项目的allprojects或subprojects代码块继承公共配置。
核心配置示例:
settings.gradle
rootProject.name = 'my-multiproject'
include(':common', ':data', ':core', ':web')
根项目 build.gradle
plugins {
id 'java-library'
}
// 给所有子项目统一配置
subprojects {
apply plugin: 'java-library'
group = 'com.example'
version = '1.0.0'
repositories {
mavenCentral()
}
dependencies {
testImplementation 'junit:junit:4.13.2'
}
}
子项目 core/build.gradle
dependencies {
implementation project(':common') // 依赖同一工程下的common模块
implementation 'com.google.guava:guava:31.1-jre'
}
依赖引用使用project(':模块名')语法,Gradle 会自动处理模块间编译顺序和增量编译。
关键概念:根项目的
build.gradle默认只配置自身,若要对所有子项目生效,需使用subprojects { };使用allprojects { }则同时包含根项目和子项目,通常用于统一仓库和全局插件。
1.4 多模块项目结构示意图
下图展示了一个典型三层次多模块结构,包含公共模块、核心业务模块和展示模块,并标注了依赖方向。
1.5 常见错误与调试
- 子项目未被识别:未在
settings.gradle中include对应模块,导致 Gradle 提示“Project with path ‘:xxx’ not found”。 - 循环依赖:模块 A 依赖 B,B 又依赖 A,Gradle 在编译时抛出
Circular dependency错误。解决方法是提取公共部分到独立的:common模块。 - 版本不一致:不同子项目使用了不同版本的三方库,导致运行时冲突。应统一在根项目或版本目录中管理依赖版本。
调试命令:执行
gradle projects 可查看当前工程包含的所有子项目及其分组结构。若子项目列表与预期不符,优先检查 settings.gradle 的正确性。
1.6 性能与稳定性考量
| 关注点 | 优化手段 |
|---|---|
| 增量编译 | 确保子项目间依赖通过 project() 声明,Gradle 会自动计算变更范围,避免全量编译。 |
| 并行构建 | 在 gradle.properties 中设置 org.gradle.parallel=true,让无依赖关系的子项目并行编译。 |
| 配置缓存 | 开启 org.gradle.caching=true 和 org.gradle.configuration-cache=true,降低重复配置开销。 |