Android Gradle 构建优化

3,266 阅读5分钟

Android Gradle 构建优化

背景

本地构建的时候一次需要比较长的时间,存在优化空间。可以使用 --profile 参数来分析构建过程中各个任务的耗时情况,比如 ./gradlew assembleDebug --profile

本文基于的硬件条件:

  • MacBook Pro 2017
  • 8G + 256G

现状

./gradlew assembleDebug --profile

增量编译:

首次编译(无缓存):

开发时构建优化

开发时的 Gradle 构建优化比较有意义,构建测试包、正式包的时候并不需要那么关注构建时间。可以将开发时的构建与提测构建分离,新建一个本地开发用的 productFlavor - local,本地开发时只构建 local 包,在本地开发构建过程中禁用掉不必要的任务(比如 lint, multidex, optimization 等),减少构建时间。

优化开发构建中的 Dalvik 可执行文件分包

https://developer.android.com/studio/build/multidex#multidexkeepproguard

新建一个 local 的 productFlavor,设置 minSdkVersion 为 21,这样就会启用 pre-dexing 构建功能(仅适用于 Android 5.0+),能够加快打包速度,改动如下:

禁用构建时对 /res/drawable 目录下图片的优化

Android 构建工具默认会对 drawable 下的图片进行优化,比如图片压缩转换,校验等,这个过程是比较耗时的,尤其是当图片比较多的时候。改动如下:

dexOptions 配置优化

android.dexOptions 的配置优化

一些比较老的文章还会推荐 dexOptions.incremental,这是没必要的,在 Gradle 2.1.0-rc1 (2016-04-22) 之后,incremental 增量构建已经默认启用了,不再需要显式声明。 此外,dexOptions.jumboMode 参数也是可选的,在启用 instant-run 时这个参数会默认启用

使用 Gradle 依赖缓存

Gradle 在构建时都会去拉依赖,顺序是:本地缓存 -> 远程依赖。如果使用了动态版本,那么每次 Gradle 都会拉最新的依赖,在依赖多的情况下是非常耗时的,从下面这张图可以看出(挖财宝):

从上图中可以看出,光是拉依赖就用了 3 分钟,这都还没到编译阶段,难怪打个包要六七八分钟。。。

在 Gradle 中,最好固定依赖的版本号,这样就不会每次编译都重新拉依赖,但是在目前快速迭代的背景下,一般都是锁死第一位,第二位和第三位版本号自动更新,也没法锁死依赖,只能把之前 resolutionStrategy 改一下,使用 Gradle 默认的动态版本号缓存策略(Gradle 的动态版本也会有缓存滴),改动如下:

改动之后的效果分析如下,拉依赖的时间从 3min 变成了 20s 左右,效果明显。

但是这种情况下,如果 snapshot 版本的依赖更新了,可能不会及时自动更新到本地(因为缓存),不过 Gradle 支持 --refresh-dependencies 参数,使用 ./gradlew assembleLocalDebug --refresh-dependencies 会全量刷新依赖。

gradle.properties 中的一系列优化参数(可选)

org.gradle.daemon = true

The Gradle Daemon

Gradle 是基于 JVM 的构建系统,JVM 的启动和初始化需要时间,开启 Gradle Daemon 守护进程可以节省这些时间

org.gradle.configureondemand = true

一次 Gradle 编译大概由这几部分组成:初始化、加载 setting.gradle(|| setting.gradle.kts)、加载各个模块的 build.gradle(|| build.gradle.kts)、执行一系列任务。从上图中可以看到,Configuring Projects 也是花了 5s 左右的时间,使用 org.gradle.configureondemand=true 可以加速这一过程。

org.gradle.parallel = true

启用 Gradle 并行编译,加速编译过程

org.gradle.jvmargs = -Xmx2560M

设置 Gradle 编译时使用的最大内存,大一点比较好,但是我的小本本内存只有 8G,有心无力。。。 使用大内存时 Android 编译过程中的 dexing 就不会单独开进程进行,能节省构建时间。

android.enableBuildCache = true

启用 Android 编译构建缓存,此举能极大提升构建效率,推荐! 但是我司因为统跳编译插件不支持编译缓存,因此只能禁用。

综上所述

综上所述,挖财宝中启用的开发时构建优化措施有:

  • 设置 minSdkVersion 为 21
  • /res/drawable 目录下图片的优化
  • dexOptions 配置优化
    • jumboMode = true
    • javaMaxHeapSize = "2g"
    • preDexLibraries = true
    • threadCount = 8
  • gradle.properties 配置优化
    • org.gradle.daemon = true
    • org.gradle.configureondemand = true
    • org.gradle.parallel = true
    • org.gradle.jvmargs = -Xmx2560M,大内存的话可以再大一点
    • android.enableBuildCache = true 用不了。

优化后使用 ./gradlew assembleLocalDebug 生成开发包,或者使用 ./gradlew installLocalDebug 直接安装。 提测时使用 offcialDebug 或者 qqDebug 均可。打生产包时使用 offcialRelease 或者 qqRelease

优化之后的效果如下图所示,增量编译一般可以控制在 2min 以内

增量编译:

首次编译(无缓存):

题外话

  1. 其实,提升 Gradle 构建速度的最有效途径不在上面,很简单,换个 Mac Pro 来编译就好了
  2. 上文中提到的几个 gradle.properties 中的配置,如果只想本地使用,就不要写在 gradle.properties 中,可以写在 local.properties 或 ~/.gradle/gradle.properties 中
  3. 如果条件允许的话,使用最新的 Gradle 版本吧,新版的 Gradle 构建速度绝对比旧版本的快(目前是 Gradle 5.0)
  4. 移除不必要的三方依赖
  5. 按需编译,部分 task 在开发的时候是不需要执行的,比如 lint,直接禁用掉即可,可以使用 --profile 参数进行分析,分析报告在 build/reports/profile/ 目录下
  6. 优化永无止境

参考资料

  1. 纳尼?我的Gradle build编译只要1s
  2. 6个技巧加速你的gradle编译
  3. Gradle Docs - The Gradle Daemon

Personal

  • 微信公众号:yongf666
  • 掘金:https://juejin.cn/user/78820566373432
  • 个人网站:https://www.54yongf.com/
  • 微博:http://weibo.com/2902237231
  • 知乎:http://www.zhihu.com/people/wang-yong-8-33
  • 邮箱:ScottWang1996@gmail.com
  • StackOverflow:http://stackoverflow.com/users/5304207