Android 编译这件小事
什么是编译?
我们每天都要经历数十次的编译,那么什么是编译呢?简单来说,就是把高级语言转化为机器或虚拟机能识别的低级语言的过程。在 Android 中,就是把我们认识的 Java 或 Kotlin 语言转化为 Android 虚拟机可以执行的 Dalvik 字节码的过程。
Android 中的编译过程
- 编译器将源代码转换为Dex文件,其他内容转化为已编译资源
- 打包器把Dex文件和已编译资源合成 apk。
- 打包器使用签名签署 apk。
- 打包器使用 zipalign 工具进行优化。
编译速度
大致了解了编译过程之后,我们最关心的应该就是编译速度了。编译速度的快慢直接决定了研发效率的高低,尤其是多人协作的项目。每个人节省1分钟,5个人就是5分钟,每天平均编译20次,那就是100分钟,1小时40分钟。这么看来,按时下班也有了希望。
关于编译速度,我们最关心的应该是编译 debug 包的速度,这直接影响我们的开发效率。从我们写完代码到安装到手机上生效,其中包含了两个时间,一个是编译时间,一个是安装时间。编译时间中,我们最关心的应该是增量编译(incremental build)的时间,开发阶段大部分的编译均属于此类。那么如何提高增量编译的速度才是核心。至于安装时间,可以使用好一点的测试机和升级新的工具链解决。
编译速度的优化
- 硬件加速,使用配置高的电脑进行开发工作。
- 保持 Android SDK Tools、Android Gradle Plugin 插件 为最新版本,享受 Google 的最新优化成果。
- 项目模块化。模块化的项目可以享受到 Gradle 并行编译的好处。
- 启用 Instant Run。作为 Google 官方的加速增量编译的方案,虽然一直被人诟病,但一些情况下还是可以提升编译速度的。
- 在开发阶段禁用某些 SDK,比如 Crashlytics。
- 在开发阶段使用静态的编译配置参数。例如我们项目中基本都存在 BuildConfig BUILD_TIME 参数,在 debug 环境下可直接写成0。
- 设置 minSdkVersion 为 21。根据各产品需求而定,minSdkVersion 为16的可以在开发阶段做此设置。
- 将项目中的图片转换为webp。编译阶段无需进行压缩,从而加快编译速度。
- 在开发阶段禁止自动压缩图片。发布版本前需手动设置为 true,无法使用构建类型和产品风味修改此属性。
1
2
3aaptOptions {
cruncherEnabled false
} - 加速 gradle 的一些设置
1
2
3
4
5
6
7
8
9
10开启并行编译,在多 module 项目中很实用
true =
开启 gradle 进程守护,无需每次编译都启动 gradle 进程
true =
开启按需配置
true =
设置 gradle 最大内存,并非越大越好,根据官方数据 1g 或 2g 最佳
-Xmx2g =
开启 gradle 构建缓存
true = - 如果项目中有使用 Kotlin 语言,尽量使用最新版本的 Kotlin。在 Kotlin 1.1.1 版本默认支持 JVM 的增量编译,1.2.20 默认支持 Gradle Build Cache。
- 开启 kapt 的 各种加速设置
- 编译缓存(1.2.20)在
build.gradle
文件中添加:1
2
3kapt {
useBuildCache = true
} - 并行任务(1.2.60)在
gradle.properties
文件中添加:1
true =
- 编译回避(1.3.20)在
gradle.properties
文件中添加:1
false =
- 增量编译(1.3.30)在
gradle.properties
文件中添加:1
true =
- 编译缓存(1.2.20)在
- 使用 –profile 命令分析编译过程,找到耗时的 Task,逐个进行优化。
针对模块化项目的编译优化建议
- 创建纯粹的 Java/Kotlin 库,因为纯粹的 Java/Kotlin 库的 Gradle Task 依赖树非常简单。
- 只应用需要的 Gradle 插件。
- 分离AP(Annotation Processors)到单独的 Module。从 Gradle 4.7 版本,支持 AP 的增量编译;从 Kotlin 1.3.30 版本,kapt 也支持增量编译。在
gradle.properties
中添加1
kapt.incremental.apt=true
- 只在 app module 中使用 lint。
1
2
3lintOptions {
tasks.lint.enabled = false
} - 使用 api 或 implementation 而不是 compile。
- 使用 ext 或 buildSrc 来管理版本依赖。