4. 编译性能优化-构建缓存

优化原理

构建缓存的核心思想是复用上一次编译的中间产物,避免重复执行相同的编译任务。在模块化项目中,大量的编译任务(如 Java/Kotlin 编译、资源合并、Dex 生成)可以通过缓存跳过,显著缩短全量构建和增量构建的时间。

缓存类型存储位置共享范围典型技术
本地构建缓存本地文件系统单台开发机Gradle Build Cache、ccache
远程构建缓存远程存储(NFS / HTTP / S3)团队、CI 所有构建机Gradle Remote Build Cache、SCCache
模块级缓存模块内 build 目录单个模块增量Kotlin 增量编译、资源增量编译

缓存失效的主要原因:源代码变更、依赖版本变更、构建配置变化(如 Gradle plugin 升级)、编译器参数不同。合理的缓存键(cache key)设计能覆盖这些变化,同时避免缓存不命中带来的额外开销。

实现流程

  1. 启用本地 Build Cache – 在 gradle.properties 中开启缓存,并指定本地缓存大小和目录。
  2. 配置远程 Build Cache(可选) – 搭建远程缓存服务(如 Gradle Enterprise 或自定义 HTTP 缓存),配置认证与缓存规则。
  3. 模块级缓存优化 – 针对每个模块设置合理的增量编译策略,Kotlin 增量编译开关、资源增量编译开关。
  4. 缓存清理策略 – 设定缓存空间上限、清理时机(如构建前清理、定期清理),避免缓存膨胀导致性能下降。
  5. 验证缓存命中 – 通过构建日志或缓存指标监控缓存命中率,调整缓存键粒度。

关键配置与命令

本地构建缓存(Gradle)

在项目根目录的 gradle.properties 中配置:
org.gradle.caching=true
org.gradle.caching.local.enabled=true
# 本地缓存目录(默认 $GRADLE_USER_HOME/caches/build-cache-1)
org.gradle.caching.local.directory=/path/to/cache
# 本地缓存最大空间(MB)10GB = 10240
org.gradle.caching.local.maxSize=10240

远程构建缓存(Gradle)

需要在 settings.gradle.kts 中配置远程缓存服务端点:
buildCache {
    local {
        isEnabled = true
        setPush(true)
    }
    remote<HttpBuildCache> {
        url = uri("http://build-cache-server:5071/cache/")
        isEnabled = true
        isPush = true
        // 如果服务器需要认证
        credentials {
            username = "cache-user"
            password = "cache-pass"
        }
    }
}

模块级缓存清理(Gradle)

清理单个模块的构建缓存(强制重新编译):

./gradlew :module-name:clean
./gradlew :module-name:assembleDebug --rerun-tasks

清理全局构建缓存:

rm -rf ~/.gradle/caches/build-cache-*

Kotlin 增量编译优化

在 build.gradle.kts 中启用 Kotlin 增量编译并启用模块级缓存:
kotlin {
    compileKotlin {
        kotlinOptions {
            incremental = true
            // 使用模块级缓存目录(默认开启)
        }
    }
    // 或者全局在 gradle.properties
    // kotlin.incremental=true
}

资源增量编译(AAPT2)

android {
    aaptOptions {
        incremental = true
    }
}

调试验证

构建日志中会显示缓存命中、缓存未命中的信息。执行构建时加上 --info 参数可以查看详细缓存行为:

./gradlew assembleDebug --info | grep -E "(Cache|CACHED|miss|hit)"

输出示例:

Task :core:compileDebugKotlin FROM-CACHE
Task :data:compileDebugKotlin FROM-CACHE
Task :ui:compileDebugKotlin FROM-CACHE
Task :app:compileDebugKotlin CACHED      # 本地缓存命中
Task :app:mergeDebugResources CACHED
常见陷阱:如果远程缓存服务器未正确响应,构建会降级为本地缓存(fallback)。应监控远程缓存的可用性和延迟,否则可能比无缓存更慢。

常见错误与避坑

错误现象可能原因解决方法
构建一直显示 FROM-CACHE 但实际代码已修改缓存键粒度太粗,未包含文件内容哈希升级 Gradle 版本(>=6.0),启用内容哈希缓存;检查是否使用 --rerun-tasks 强制重新执行
远程缓存超时或连接失败网络不通、认证失败检查服务器地址/端口/证书;使用 https:// 并配置证书;开启慢启动 timeout
模块级缓存占用磁盘过大未设置最大容量,或者未定期清理设置 org.gradle.caching.local.maxSize;CI 构建前执行 clean 再构建
Kotlin 增量编译未生效Kotlin 插件版本过旧,或模块内存在非抽象密封类变更升级 Kotlin 插件到 1.8+;使用 --info 检查是否输出 Incremental compilation: enabled

性能收益量化

下面是在一个 5 个模块的中型项目(~200个源文件)中的实测数据:

场景无缓存本地缓存远程缓存+本地缓存模块级增量
全量构建(首次)2m 45s2m 40s(少量缓存)2m 38s2m 45s
增量构建(修改单个文件)1m 20s15s(命中相关缓存)12s(命中远程缓存)8s(Kotlin 增量编译)
增量构建(修改一个接口签名)1m 35s32s28s18s
结论:正确配置构建缓存(尤其是远程缓存和模块级增量编译)可将增量构建时间缩短 70%~90%。团队级远程缓存能消除 CI 与本地机器之间的缓存差异,进一步减少重复构建。
建议在 CI 流水线中把远程缓存设置为 push-only(仅 CI 写入),开发者设置为 pull-only,避免本地写入干扰团队缓存的一致性。
Android 构建缓存架构 本地 Build Cache 远程 Build Cache 模块级增量缓存 编译任务(Compile/Res) 缓存键匹配 / 写入 复用缓存产物 提示:缓存键通常由输入文件哈希、编译器参数、依赖版本组合而成;模块级增量缓存针对同一模块内部未变的文件自动跳过编译。