【Gradle-14】编译优化之Gradle最佳配置实践

本文为稀土掘金技术社区首发签约文章,30天内禁止转载,30天后未获授权禁止转载,侵权必究!

1、写在前面

提升编译速度最快的是升级电脑配置,其次是接入Gradle Enterprice企业版,这两者简单粗暴,带来的效果也非常明显,但是这两者的成本却不一定是所有人都能接受的,特别是目前大家都在搞降本增效,所以想要靠外在力量来提升编译速度更是难上加难,遂,有了本文,通过Gradle配置最佳实践的方式来帮助大家低成本的提升编译速度。

钱不是问题,问题是没钱。

2、为什么要做编译优化

性能优化的三驾马车是启动、包大小和内存,这三个指标都跟用户的体验息息相关,所以编译优化就变成了重要不紧急的事情,但编译优化也能带来诸多好处,主要有两点:

  1. 提升开发效率:开发者可以更快地编译和运行代码,这意味着对代码的变更可以更快的得到反馈,从而加速开发周期,提升个人和整个团队的生产力;
  2. 提升开发体验:减少了等待时间,提升个人和整个团队的幸福感和满意度;

影响摸鱼?干完不是可以摸的更舒畅吗...

3、影响编译速度的因素

主要有以下几个方面:

  • 硬件性能:CPU、RAM等;
  • 构建配置:缓存、增量编译等;
  • 项目:项目的大小和复杂度,代码量、模块化、依赖管理等;
  • 其他:网络速度,下载慢或者找不到等;

4、编译优化原则

所以,基于上面影响编译速度的因素来看,编译优化原则也可以推导为两个方面:

  1. 复用:提升复用率,不仅是代码的复用,也是编译产物的复用,不仅关注首次编译速度,也要关注二次编译速度;
  2. 更少:减少参与编译的文件,越少则处理越快,不仅是代码文件,还有资源文件;

5、最佳实践

ok,上面铺垫了这么多,下面给大家介绍一些实用的构建配置。

5.1、升级版本

5.1.1、升级Gradle

Gradle作为一个构建工具,提升构建性能可以说是基础操作,基本每个大版本都会带来各种各样的性能提升,比如Gradle 6.6以后对配置阶段的提升,7.0以后对Kotlin编译的提升,8.0以后对增量编译的进一步提升等等,这些都是构建工具之外无法做到的,所以,如果你的Gradle还不是最新版本,有条件的话一定要试试。

虽然升级Gradle有一定的适配成本,但是如果不升,长此以往,技术负债只会越来越多。

Keeping up with Gradle version upgrades is low risk because the Gradle team ensures backwards compatibility between minor versions of Gradle.

官方还说是低风险,😆

5.1.2、升级Java

Gradle是运行在Java虚拟机上的,即JVM,Java性能的提升也会有利于Gradle。

5.1.3、升级Plugin

同Gradle,一般也都是跟随着Gradle升一波。

5.2、开启并行编译

Gradle默认一次只执行一个Task,即串行,那我们就可以通过配置让Gradle并行来执行Task,从而提升构建效率,缩短构建时间。

gradle.properties文件中添加:

ini 复制代码
org.gradle.parallel=true

5.3、开启守护进程

开启守护进程之后,Gradle不仅可以更好的缓存构建信息,而且运行在后台,不用每次构建都去初始化然后再启动JVM了。

gradle.properties文件中添加:

ini 复制代码
org.gradle.daemon=true

以上两个配置,在GradleX项目中测验,增量编译速度提升60%以上,实际以自己项目为准。

5.4、启用配置缓存

配置缓存是Gradle 6.6以后提供的能力。

当没有构建配置发生变化的时候,比如构建脚本,Gradle会直接跳过配置阶段,从而带来性能的提升。

构建配置主要是scripts和properties,一般业务开发也不会动这个,所以还是非常有用的。

ini 复制代码
org.gradle.unsafe.configuration-cache=true

5.5、启用构建缓存

同一个Task的输入不变的情况下,Gradle直接去检索缓存中检索输出,就不用再次执行该Task了。

ini 复制代码
org.gradle.caching=true

5.6、启用增量编译

Gradle 4.10及以上默认开启,如果是4.10以下的版本,可以加上如下代码手动开启。

build.gradle:

ini 复制代码
tasks.withType(JavaCompile).configureEach {
    options.incremental = true
}

关于Task的增量编译,请看这篇:【Gradle-7】Gradle构建核心之Task指南 - 掘金

5.7、增加JVM堆大小

默认是512m的堆大小,多数情况下并不够用,可以通过jvmargs来调整内存堆大小。

ini 复制代码
org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8

2048m=2g,也可以写成

ini 复制代码
org.gradle.jvmargs=-Xmx2g -Dfile.encoding=UTF-8

这个大小可以根据自己电脑配置来调整。

5.8、使用 JVM 并行垃圾回收器

通过配置Gradle所用的最佳JVM垃圾回收器,可以提升构建性能。-XX:+UseParallelGC

ini 复制代码
org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 -XX:+UseParallelGC

5.9、增加Android Studio运行内存

上面提到增加Gradle的内存堆大小,AS的运行内存空间同样也可以调整。

调整AS内存的配置文件在AS > Contents > bin > studio.vmoptions文件中

打开文件:

diff 复制代码
-Xms256m
-Xmx1280m
-XX:ReservedCodeCacheSize=512m
-XX:+UseG1GC
-XX:SoftRefLRUPolicyMSPerMB=50
-XX:CICompilerCount=2
-XX:+HeapDumpOnOutOfMemoryError
.....

这里主要有两个参数需要关注:

  • -Xms256m:初始堆内存大小;
  • -Xmx1280m:最大堆内存大小;

可以修改为:

diff 复制代码
-Xms256m
-Xmx2048m

然后保存并重启AS生效即可。

Android Studio的运行内存占比可以在AS底部的工具栏上右键,把Memory Indicator勾选上,然后在右下角就可以显示出来了。

5.10、优化依赖解析

5.10.1、删除无用的依赖

可以通过Gradle Lint Plugin来识别未使用的依赖项,然后删除,从而减少构建时间。

5.10.2、优化存储库顺序

Gradle在解析依赖时,默认按照配置声明的仓库地址顺序去搜索,即自上而下,所以为了减少搜索依赖项花费的时间,一般会按照项目中使用的依赖项归属的仓库来排列仓库地址顺序。

Android开发用的比较多的是google(),其次是mavenCentral(),所以这俩应该是在前面的,其他的看自己项目依赖情况排列。

scss 复制代码
    repositories {
        google()
        mavenCentral()
        // others
    }

5.10.3、优化依赖的下载速度

由于有些仓库的地址是在国外的,导致很多同学就经常遇到下载依赖很慢的情况,所以我们可以使用一些国内的镜像来提升下载速度。

rust 复制代码
    repositories {
        // 阿里云仓库
        maven { url 'https://maven.aliyun.com/repository/public/' }
        google()
        mavenCentral()
    }

阿里云镜像整理:juejin.cn/post/690747...

5.10.4、优化依赖版本

避免使用动态版本(2.+)和快照版本(2-SNAPSHOT),这样可以避免Gradle在构建的时候去检索新版本,默认是24小时检查一次。

比如微信的SDK,为了让大家及时更新就使用了动态版本:

arduino 复制代码
api 'com.tencent.mm.opensdk:wechat-sdk-android:+'

看下SDK的介绍,直接使用具体的版本:

arduino 复制代码
api 'com.tencent.mm.opensdk:wechat-sdk-android:6.8.23'

关于动态版本和快照版本的检索时效也可以通过以下配置来设置:

arduino 复制代码
configurations.all {
		// 动态版本缓存时效
    resolutionStrategy.cacheDynamicVersionsFor(10, "minutes")
    // 快照版本缓存时效
    resolutionStrategy.cacheChangingModulesFor(4, "hours")
}

5.11、避免编译不必要的资源

避免编译和打包不测试的资源(例如,其他语言本地化和屏幕密度资源)。可以仅为"dev"变种的版本指定一个语言资源和屏幕密度,如下:

arduino 复制代码
android {
    productFlavors {
        dev {
            ...
            resourceConfigurations "en", "xxhdpi"
        }
    }
}

5.12、按需编译

道理同上,使用assembleDebug或者assembleRelease,来代替assemble

同理,还有debugImplementationabiFilters

以及按需依赖插件:

scss 复制代码
if(debug){
  	apply xxx
}

5.13、禁用不需要的Task

还有Task也是同理,比如跳过LintTest相关的task, 以加速编译。

less 复制代码
     //跳过Lint和Test相关的task, 以加速编译
     if (isDebug()) {
         gradle.taskGraph.whenReady {
             tasks.each { task ->
                 if (task.name.contains("Test") || task.name.contains("Lint")) {
                     task.enabled = false
                 }
             }
         }
     }

5.14、将图片转换为WebP格式

WebP是一种既可以提供有损压缩(像 JPEG 一样)也可以提供透明度(像 PNG 一样)的图片文件格式。与 JPEG或PNG相比,WebP格式可以提供更好的压缩效果。

减小图片文件大小可以加快构建速度(无需在构建时进行压缩),尤其是当应用使用大量图片资源时。

Android Studio中:

css 复制代码
选中图片 > 右键 > Convert to WebP

5.15、停用PNG处理

即使不将PNG图片转换为WebP格式,仍然可以在每次构建应用时停用自动图片压缩,以加快构建速度。

arduino 复制代码
android {
    buildTypes {
        release {
            crunchPngs false
        }
    }
}

5.16、使用非传递R类

使用非传递 R 类可为具有多个模块的应用构建更快的 build。这样做有助于确保每个模块的 R 类仅包含对其自身资源的引用,而不会从其依赖项中提取引用,从而帮助防止资源重复。这样可以获得更快的 build,以及避免编译的相应优势。在Android Gradle 插件 8.0.0 及更高版本中的默认开启。

ini 复制代码
android.nonTransitiveRClass=true

从 Android Studio Bumblebee 开始,新项目的非传递 R 类默认处于开启状态。 对于使用早期版本的 Android Studio 创建的项目,可以在 Refactor > Migrate to Non-transitive R Classes,将项目更新为使用非传递 R 类。

5.17、停用Jetifier标志

由于大多数项目都直接使用 AndroidX 库,因此可以移除Jetifier标志,以便获得更好的构建性能。

ini 复制代码
android.enableJetifier=false

Jetifier是把support包转成AndroidX的工具,现在基本上都已经适配AndroidX了,可以关掉,从而提升构建性能。

如果你是开启的,即android.enableJetifier=true,可以使用Build Analyzer工具来查看是否会有警告,即是否可以移除。

5.18、使用KSP代替kapt

kapt是Kotlin注解处理工具,kapt的运行速度明显慢于Kotlin Symbol Processor (KSP)。官方:速度提升多达2倍

5.18.1、启用KSP

project:

bash 复制代码
plugins {
    id("com.google.devtools.ksp") version "1.8.10-1.0.9" apply false
}

app:

bash 复制代码
plugins {
    id("com.google.devtools.ksp")
}

5.18.2、使用KSP

scss 复制代码
dependencies {
  	// kapt
    kapt("androidx.room:room-compiler:2.5.0")
    // ksp
    ksp("androidx.room:room-compiler:2.5.0")
}

就这么简单,然后移除kapt相关的配置即可。前提是使用的库已经适配了KSP,主流的基本已经适配。

5.18.3、K2

kotlin 1.9.20中K2已经处于beta版本了,会有进一步的性能提升。

尝鲜可以在gradle.properties中加上以下配置:

ini 复制代码
kotlin.experimental.tryK2=true
kapt.use.k2=true

更多可查看:kotlin-k2-compiler

5.19、去掉动态配置

每次编译之后会产生不同结果的配置。

5.19.1、动态的FileName

比如每次打包会把apk的名字加上当前构建时间,这样不仅会产生大量的文件,而且每次都要更新,影响编译速度。

arduino 复制代码
applicationVariants.all { variant ->
    variant.outputs.all { output ->
        outputFileName = "${variant.name}-${buildTime()}.apk"
    }
}

static def buildTime() {
    return new Date().format("MMdd_HHmm", TimeZone.default)
}

5.19.2、动态的VersionName

scss 复制代码
defaultConfig {
    versionCode $buildTime().toInteger()
    versionName '1.0.$buildTime()'
}

static def buildTime() {
    return new Date().format("MMdd_HHmm", TimeZone.default).toInteger()
}

道理同上,动态的versionCodeversionName也是不推荐的。

5.20、其他

5.20.1、开启离线模式

老版本可以在Settings>Build>Gradle中把offline work勾选上,或者在编译脚本里加上--offline

5.20.2、优化DexOptions

老版本配置:

arduino 复制代码
android {
    dexOptions {
        // 使用增量模式构建
        incremental true
        // 最大堆内存
        javaMaxHeapSize "4g"
        // 是否支持大工程模式
        jumboMode = true
        // 预编译
        preDexLibraries = true
        // 线程数
        threadCount = 8
        // 进程数
        maxProcessCount 4
    }
}

5.20.3、模块化

一般项目中使用了模块化的这种架构设计,都会做源码依赖和AAR依赖的切换,来提升构建速度,主要是减少了参与编译的代码,再加上缓存,所以提升效果明显。这个后面单独写一篇介绍下。

5.20.4、构建分析

使用构建分析来解决项目中的构建耗时点,以及不合理配置,参考:【Gradle-10】不可忽视的构建分析

6、总结

本文先是介绍了为什么要做编译优化,然后分析了影响编译速度的因素有哪些,从最少、复用的构建原则入手,详细的为大家介绍了一些低成本且实用的最佳实践指南。如果你还没有优化过,可以实操起来了~

如果本文对你有一点点帮助,请不要吝啬你的点赞和关注~

7、GitHub

github.com/yechaoa/Gra...

8、相关文档

相关推荐
coder_pig1 小时前
🤡 公司Android老项目升级踩坑小记
android·flutter·gradle
死就死在补习班2 小时前
Android系统源码分析Input - InputReader读取事件
android
死就死在补习班2 小时前
Android系统源码分析Input - InputChannel通信
android
死就死在补习班2 小时前
Android系统源码分析Input - 设备添加流程
android
死就死在补习班2 小时前
Android系统源码分析Input - 启动流程
android
tom4i3 小时前
Launcher3 to Launchpad 01 布局修改
android
雨白3 小时前
OkHttpClient 核心配置详解
android·okhttp
淡淡的香烟3 小时前
Android auncher3实现简单的负一屏功能
android
RabbitYao4 小时前
Android 项目 通过 AndroidStringsTool 更新多语言词条
android·python
RabbitYao4 小时前
使用 Gemini 及 Python 更新 Android 多语言 Excel 文件
android·python