编译优化与构建加速:Gradle构建优化

说实话,做Android开发这么多年,我最怕的就是等编译。尤其是大项目,改一行代码,等三分钟编译,那种感觉就像看着沙漏等下班。今天我们就聊聊怎么让Gradle跑得快一点,再快一点。

增量编译:别每次都从头来

增量编译这个概念,说白了就是「只编译改动的部分」。Gradle其实默认就支持,但很多人没用好。

我遇到过最典型的场景:一个同事改了资源文件里的一个字符串,结果整个项目重新编译了一遍。为什么?因为他把资源文件放在了src/main/res下,但Gradle的增量编译对资源文件的变更比较敏感。

我的建议:尽量把代码和资源分离。Java/Kotlin代码改动,增量编译效果很好。资源文件改动,尤其是布局和drawable,容易触发全量编译。

怎么确认增量编译生效了?看编译日志:

// 增量编译生效时,你会看到类似这样的输出
:app:compileDebugKotlin UP-TO-DATE
:app:compileDebugJavaWithJavac UP-TO-DATE

如果看到的是EXECUTING而不是UP-TO-DATE,说明这次没有走增量。

并行编译:让CPU多干点活

现在的开发机哪个不是8核16线程?但Gradle默认只用一个线程干活,你说浪费不浪费?

gradle.properties里加上这两行:

org.gradle.parallel=true
org.gradle.workers.max=4

并行编译的原理,是把多个模块的编译任务拆开,同时跑。比如你的项目有appcorenetwork三个模块,它们之间如果没有依赖关系,就可以同时编译。

注意:并行编译不是越大越好。workers数量建议设为CPU核心数的一半。我曾经设成8,结果编译时电脑卡得鼠标都动不了。

KSP vs KAPT:注解处理器的选择

这个我得好好说说。KAPT(Kotlin Annotation Processing Tool)是Google早期推出的方案,但它有个问题——它会把Kotlin代码先转成Java桩文件,然后再走Java的注解处理器流程。这一步非常慢。

KSP(Kotlin Symbol Processing)是后来出的,它直接处理Kotlin的语法树,跳过了转Java那一步。速度提升非常明显。

对比项 KAPT KSP
处理速度 慢(需要生成桩文件) 快(直接处理语法树)
兼容性 支持所有Java注解处理器 需要库作者适配
编译时间 基准 通常快2-3倍
适用场景 老项目、未适配KSP的库 新项目、已适配的库

我个人的习惯是:能用KSP就用KSP。像Room、Glide、Moshi这些主流库,早就支持KSP了。迁移也很简单:

// 替换前(KAPT)
plugins {
    id 'org.jetbrains.kotlin.kapt'
}

dependencies {
    kapt "androidx.room:room-compiler:2.6.0"
}

// 替换后(KSP)
plugins {
    id 'com.google.devtools.ksp'
}

dependencies {
    ksp "androidx.room:room-compiler:2.6.0"
}

迁移之后,我那个项目的编译时间从4分20秒降到了1分50秒。嗯,这个数字我记得很清楚。

R8全量混淆与资源缩减

R8是ProGuard的升级版,它不只是做混淆,还做代码优化和资源缩减。说白了,就是帮你把APK「瘦身」。

全量混淆的意思是,不只是混淆你的代码,还把第三方库的代码也混淆了。这样APK体积更小,反编译也更难。

build.gradle里这样配置:

android {
    buildTypes {
        release {
            minifyEnabled true
            shrinkResources true
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
}

注意shrinkResources true这个配置。它会在混淆之后,自动移除未被引用的资源文件。比如你项目里有一百张图片,但实际只用了五十张,剩下的就会被删掉。

避坑指南:我曾经因为shrinkResources踩过坑。项目里用getIdentifier()动态获取资源ID,结果R8把那些资源当成「未使用」给删了。运行时直接崩溃。解决方案是在proguard-rules.pro里加规则:
-keep class **.R$* { *; }

R8的优化级别有几种:

  • ProGuard模式:兼容旧规则,优化较少
  • R8全量模式:默认开启,优化力度大
  • R8完全模式android.enableR8.fullMode=true,最激进

我建议新项目直接用R8全量模式。老项目可以先在测试环境跑一遍,看看有没有问题。

知识体系总览

下面这张图,是我对编译优化这块的理解。你可以把它当成一个检查清单:

Gradle构建优化知识体系 编译加速 注解处理优化 打包与资源优化 增量编译 并行编译 (parallel=true) 构建缓存 (build-cache) KAPT(传统方案) KSP(推荐方案) 迁移适配(Room/Glide等) R8全量混淆 资源缩减 (shrinkResources) ProGuard规则配置 目标:编译时间缩短50%+,APK体积减少30%+ 核心原则:能复用不复用,能并行不串行,能精简不冗余

你看,整个体系其实就三大块:编译加速、注解处理优化、打包优化。每一块都有对应的落地手段。

最后说一句:优化不是一蹴而就的。我建议你先做一次编译耗时分析,找到瓶颈在哪,然后对症下药。别一上来就全改,改完发现某个配置不兼容,又得回滚,那才叫浪费时间。

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