1. 模块化拆分原则:单一职责与分层依赖
Gradle 模块拆分是大型 Android 工程治理的第一道防线。合理的模块划分能让构建加速、团队解耦、复用提升;反之则会出现循环依赖、构建缓慢、改一处坏全局的问题。本章从三个核心维度出发:划分原则、单一职责、分层依赖,并给出完整的实现流程与避坑指南。
1.1 模块划分三大原则
- 高内聚低耦合 — 模块内的类、资源、功能紧密关联,对外暴露最小接口。
- 业务与基础设施分离 — 业务模块只依赖抽象接口,基础设施模块提供实现(网络、数据库、DI等)。
- 复用粒度适中 — 太细导致模块爆炸,太粗失去拆分意义;以“业务域”为粒度(如登录、订单、支付)。
关键衡量指标:如果两个模块的语法依赖(import)图出现两个以上交叉引用,就应该考虑合并或提取中间抽象层。
1.2 单一职责:每个模块只做一件事
“单一职责”在 Gradle 模块化中意味着:一个模块不应同时承担业务逻辑、数据缓存和 UI 渲染。典型的职责边界如下:
| 层次 | 模块职责 | 典型 Gradle 模块名 |
|---|---|---|
| 应用壳 | 组装、启动、权限声明 | app |
| 业务特性 | 用户故事、页面流、状态管理 | :feature:profile、:feature:checkout |
| 核心数据 | 仓库实现、API 客户端、本地存储 | :core:data、:core:network |
| 公共 UI | 共享组件、主题、图标 | :core:ui、:core:designsystem |
| 基础库 | 无业务含义的工具、扩展、DI 容器 | :libraries:utils、:libraries:di |
实践建议:每个模块的 build.gradle(.kts) 中只声明与本职责直接相关的依赖。如果发现模块同时需要
retrofit 和 room,说明它承担了数据和缓存两个职责,应拆分。
1.3 分层依赖:自上而下,不跨层
依赖方向必须保持单向:
- 顶层 — 应用壳依赖所有
:feature:*模块。 - 中间层 — 业务模块依赖
:core:data、:core:ui,不能反向依赖。 - 底层 — 核心模块依赖
:libraries:*或纯 Kotlin/Java 库。
跨层依赖(如业务模块直接依赖库模块中的 AppCompat)是允许的,但业务模块绝不能依赖另一个业务模块的 internal 包。消除循环依赖的唯一手段是引入接口模块或依赖倒置。
常见陷阱:
- 业务模块 A 引用业务模块 B 的
- 两个核心模块互相依赖(如
- 底层模块引用了上层模块的 API → 打破分层,应抽取共同接口到独立模块。
- 业务模块 A 引用业务模块 B 的
internal 类 → 导致模块间紧耦合。- 两个核心模块互相依赖(如
:core:network 和 :core:cache 双向引用)→ 构建失败。- 底层模块引用了上层模块的 API → 打破分层,应抽取共同接口到独立模块。
1.4 分层依赖结构图
1.5 实现流程:从单模块到多模块
- 识别模块边界 — 在当前的
app/src/main/java下通过 package 组织,判断哪些功能可被独立复用或独立部署。 - 创建 Gradle 子项目 — 在
settings.gradle.kts中声明:include(":feature:login") include(":core:data") include(":libraries:utils") - 移动代码 — 将业务逻辑、资源文件按包迁移至对应模块,调整 import 语句。
- 配置依赖 — 在
:feature:login的 build.gradle.kts 中添加implementation(project(":core:data")),并确保无循环引用。 - 验证构建 — 运行
./gradlew :app:assembleDebug并检查依赖图中是否有cycle警告。
里程碑检查点:使用 Gradle 内置 task
dependencies 查看依赖树,确认所有依赖方向均为单向。./gradlew :app:dependencies --configuration debugRuntimeClasspath
1.6 常见错误与性能优化
| 错误类型 | 表现 | 解决方案 |
|---|---|---|
| 过度拆分 | 模块数 > 30,build.gradle 配置爆炸 | 合并复用性低的模块,保持粒度在业务域级 |
| 循环依赖 | Gradle 构建失败:Cycle detected |
提取接口模块或使用依赖倒置 |
| 跨层直接依赖 | feature 模块直接引用第三方库 | 强制通过 core 模块封装后再暴露 |
| 构建速度下降 | 增量构建失效,全量编译 | 配置 --build-cache,使用 api 谨慎,多用 implementation |
1.7 部署与调试要点
模块拆分后,CI/CD 流水线应增加模块级并行编译。同时借助 gradle-module-plugins 统一版本号,确保每个模块 publish 时无遗漏。调试时可通过 --stacktrace 定位模块间配置冲突。
动态交付:分层依赖是多模块动态交付(如 Play Feature Delivery)的前提。每个特性模块若保持单一职责,可独立打包为 on-demand 功能模块。