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 分层依赖:自上而下,不跨层

依赖方向必须保持单向:

  1. 顶层 — 应用壳依赖所有 :feature:* 模块。
  2. 中间层 — 业务模块依赖 :core:data、:core:ui,不能反向依赖。
  3. 底层 — 核心模块依赖 :libraries:* 或纯 Kotlin/Java 库。

跨层依赖(如业务模块直接依赖库模块中的 AppCompat)是允许的,但业务模块绝不能依赖另一个业务模块的 internal 包。消除循环依赖的唯一手段是引入接口模块或依赖倒置。

常见陷阱:
- 业务模块 A 引用业务模块 B 的 internal 类 → 导致模块间紧耦合。
- 两个核心模块互相依赖(如 :core:network 和 :core:cache 双向引用)→ 构建失败。
- 底层模块引用了上层模块的 API → 打破分层,应抽取共同接口到独立模块。

1.4 分层依赖结构图

Gradle 模块分层依赖(自上而下) :app ▸ 应用壳 :feature:login :feature:order :core:data :core:ui :libraries:utils :libraries:di 第三方依赖(Retrofit / Room / Hilt) 依赖顺序:app → feature → core → library → 第三方

1.5 实现流程:从单模块到多模块

  1. 识别模块边界 — 在当前的 app/src/main/java 下通过 package 组织,判断哪些功能可被独立复用或独立部署。
  2. 创建 Gradle 子项目 — 在 settings.gradle.kts 中声明:
    include(":feature:login")
    include(":core:data")
    include(":libraries:utils")
  3. 移动代码 — 将业务逻辑、资源文件按包迁移至对应模块,调整 import 语句。
  4. 配置依赖 — 在 :feature:login 的 build.gradle.kts 中添加 implementation(project(":core:data")),并确保无循环引用。
  5. 验证构建 — 运行 ./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 功能模块。