29、构建系统实战案例:大型项目构建优化案例、模块化构建落地案例、CI/CD流水线搭建案例、构建问题排查案例

说实话,前面讲了那么多理论,今天终于到了我最喜欢的环节——实战。

你想想看,学了那么多 Gradle 配置、Task 编排、依赖管理,最终不就是为了解决实际项目中的问题吗?

这一章,我把自己这些年踩过的坑、填过的土,浓缩成四个典型案例。每个案例都是真实项目里摸爬滚打出来的,希望能帮你少走弯路。

一、大型项目构建优化案例

先说说大型项目。什么叫大型?我经历过一个项目,光 module 就有 80 多个,全量构建一次要 40 分钟。嗯,你没看错,40 分钟。每次改一行代码,等构建的时间够泡三杯咖啡了。

1.1 问题诊断:先找到瓶颈在哪

优化之前,得先知道慢在哪。我个人习惯用 --profile--scan 两个参数。

// 生成构建报告
./gradlew assembleDebug --profile --scan

// 分析报告中的关键指标
// 1. 总耗时
// 2. 每个 Task 的执行时间
// 3. 配置阶段耗时
// 4. 依赖解析耗时

我记得第一次跑扫描,发现一个惊人的事实:光依赖解析就占了 12 分钟。为什么?因为项目里大量使用了动态版本号,比如 2.1.+ 这种写法。每次构建都要去远程仓库检查最新版本,能不慢吗?

避坑指南: 我曾经在一个项目中,因为用了 30 多个动态依赖,导致每次构建都要花 5 分钟去解析版本。后来全部改成固定版本号,构建时间直接砍掉 40%。

1.2 优化策略:三板斧

第一板斧:配置缓存。说白了,就是让 Gradle 记住上次的配置结果,没变化就别重新算。

// gradle.properties
org.gradle.configuration-cache=true
org.gradle.configuration-cache.parallel=true

第二板斧:并行构建。这个默认是关的,得手动开。

// gradle.properties
org.gradle.parallel=true
org.gradle.workers.max=4  // 根据 CPU 核心数调整

第三板斧:按需配置。只配置当前需要的 module,别把 80 个 module 全加载了。

// settings.gradle.kts
include(":app")
include(":core:network")
// 不要 include 所有 module,只 include 需要的
优化效果: 经过这三板斧,40 分钟的构建时间降到了 12 分钟。虽然还是有点慢,但至少能接受了。

二、模块化构建落地案例

模块化,说白了就是把一个大项目拆成多个小模块。但怎么拆?拆到什么粒度?这里头有讲究。

2.1 模块划分原则

我个人习惯按业务功能划分,而不是按层划分。举个例子:

错误做法 正确做法
  • :data
  • :domain
  • :presentation
  • :feature:login
  • :feature:home
  • :feature:profile
  • :core:network
  • :core:database

为什么?因为按层划分,改一个登录功能,你得同时改 data、domain、presentation 三个模块。而按业务划分,改登录只需要改 :feature:login 一个模块。你想想看,哪个更清晰?

2.2 模块间通信

模块化最大的坑就是模块间耦合。我曾经在一个项目里,看到 :feature:login 直接依赖了 :feature:home 的某个类。这哪是模块化?这是把大泥球拆成了小泥球。

正确的做法是:模块间通过接口通信,不直接依赖实现。

// :core:router 模块中定义接口
interface LoginRouter {
    fun navigateToHome(context: Context)
}

// :feature:login 中调用
class LoginViewModel(private val router: LoginRouter) {
    fun onLoginSuccess() {
        router.navigateToHome(context)
    }
}

// :app 中实现依赖注入
class AppLoginRouter : LoginRouter {
    override fun navigateToHome(context: Context) {
        context.startActivity(Intent(context, HomeActivity::class.java))
    }
}
小技巧: 模块化构建时,建议用 api 而不是 implementation 暴露接口模块。这样下游模块能直接使用接口,不用重复依赖。

三、CI/CD 流水线搭建案例

CI/CD 流水线,说白了就是让机器帮你做重复劳动。我见过太多团队,每天手动打包、手动测试、手动发布。嗯,效率低不说,还容易出错。

3.1 流水线设计

一个标准的 Android CI/CD 流水线,我建议分这几个阶段:

// 以 GitHub Actions 为例
name: Android CI

on:
  push:
    branches: [ main, develop ]
  pull_request:
    branches: [ main ]

jobs:
  lint:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - name: Run lint
        run: ./gradlew lint

  unit-test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - name: Run unit tests
        run: ./gradlew test

  build:
    needs: [lint, unit-test]
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - name: Build APK
        run: ./gradlew assembleRelease
      - name: Upload APK
        uses: actions/upload-artifact@v3
        with:
          name: app-release.apk
          path: app/build/outputs/apk/release/

3.2 关键配置

搭建流水线时,有几个点特别容易踩坑:

  • 缓存配置:每次 CI 都重新下载依赖?太慢了。记得缓存 ~/.gradle/caches 目录。
  • 并行任务:lint 和 unit test 可以并行跑,别串行。
  • 构建变体:CI 上只构建 release 变体,debug 变体留给本地开发。
避坑指南: 我曾经在 CI 上忘记配置 Android SDK 路径,结果构建失败。后来在 local.properties 中显式指定了 SDK 路径,或者在 CI 环境变量中设置 ANDROID_HOME

四、构建问题排查案例

最后说说排查问题。构建出问题了,别慌。我有一套排查流程,分享给你。

4.1 常见问题及解法

问题现象 可能原因 排查方法
构建卡在某个 Task 不动 Task 配置了 dependsOn 循环依赖 ./gradlew :app:dependencies 查看依赖树
依赖冲突报错 多个模块依赖了不同版本的库 ./gradlew :app:dependencyInsight --dependency xxx 定位
构建速度突然变慢 可能开启了 --no-daemon 或缓存被清空 检查 ~/.gradle/daemon 日志
APK 体积异常大 引入了不必要的 native 库或资源 ./gradlew :app:lintVitalRelease 分析

4.2 我的排查三板斧

第一斧:看日志。别只看最后几行报错,要从头看起。很多时候真正的错误在前面。

第二斧:隔离排查。把问题模块单独拎出来构建,看能不能复现。如果能,那就是模块内部的问题;如果不能,那就是模块间依赖的问题。

第三斧:回滚对比。用 Git 回滚到上次能构建的版本,然后逐步前进,找到是哪次提交引入了问题。

核心思路: 构建问题排查,说白了就是「二分法」。把问题范围不断缩小,直到找到根因。别想着一步到位,那太难了。

知识体系总览

下面这张图,是我对本章四个案例的总结。你可以把它当作一个检查清单,遇到问题时对照着看。

构建系统实战案例 大型项目优化 配置缓存 并行构建 按需配置 固定版本号 模块化构建 按业务划分 接口通信 api vs implementation 依赖注入 CI/CD流水线 lint → test → build 缓存配置 并行任务 构建变体 问题排查 看日志 隔离排查 回滚对比 依赖分析 核心原则 先诊断,后优化;先隔离,后排查;先缓存,后构建

好了,四个案例讲完了。说实话,每个案例背后都有我踩过的坑。但正是这些坑,让我对构建系统有了更深的理解。你如果遇到类似问题,不妨按我上面的思路试试。嗯,应该能帮你省不少时间。


公众号:蓝海资料掘金营,微信deep3321