Android Note - 代码构建速度优化

前言

很久没写了,这篇算是重启了

最近因为项目的编译速度越来越慢,严重到有时候甚至接近十分钟才能完成一次完整编译,就决定对着官方文档对Gradle进行一番优化。优化完成后果然构建速度得到大幅提升,遂在此记录

如何开始

由于是对照官方文档并结合实际项目进行优化,所以某些细节或者项目中没有用到的点就简略带过

保持工具处于最新状态

Android Studio 和 SDK 工具

Android Plugin for Gradle

为开发创建构建变体

即为生产环境与开发环境分开配置productFlavors

避免编译不必要的资源

您可以仅为devproductFlavors指定一个语言资源和屏幕密度

productFlavors {
    dev {
      resConfigs "en", "xxhdpi"
    }
    ...
}

为您的调试构建停用 Crashlytics(Fabric)

一般是在debug时停用

android {
  ...
  buildTypes {
    debug {
      ext.enableCrashlytics = false
    }
}

将静态构建配置值与调试构建结合使用

始终为进入 manifest 文件的属性使用静态/硬编码值,或者为您的调试构建类型使用资源文件。如果您的 manifest 文件或应用资源中的值需要随着每一个构建更新,Instant Run 将无法执行代码交换 - 它必须构建和安装新的 APK

例如,在您每次想要运行更改时,使用动态版本代码、版本名称、资源或任何其他可以更改 manifest 文件的构建逻辑都需要一个完整的 APK 构建 - 即使实际更改仅需要一个热交换,也是如此。如果您的构建配置需要此类动态属性,那么将其隔离到您的发布构建变体中并让值对您的调试构建保持静态

简单点来说,就是如果gradle构建中使用了动态构建配置,那么Instant Run就无法起到应有的作用,与直接构建新的APK没有任何区别

下面是例子

int MILLIS_IN_MINUTE = 1000 * 60
int minutesSinceEpoch = System.currentTimeMillis() / MILLIS_IN_MINUTE

android {
    ...
    defaultConfig {
        versionCode 1
        versionName "1.0"
        ...
    }

    applicationVariants.all { variant ->
        if (variant.buildType.name == "release") {
            variant.mergedFlavor.versionCode = minutesSinceEpoch;
            variant.mergedFlavor.versionName = minutesSinceEpoch + "-" + variant.flavorName;
        }
    }
}

使用静态依赖项版本

build.gradle文件中声明依赖项时,您应当避免在结尾将版本号与加号一起使用,例如com.android.tools.build:gradle:2.+ 使用动态版本号可能导致意外版本更新和难以解析版本差异,并因 Gradle 检查有无更新而减慢构建速度。您应改为使用静态/硬编码版本号

这个大家都懂,就不多说了

启用离线模式

如果您的网络连接速度比较慢,那么在 Gradle 尝试使用网络资源解析依赖项时,您的构建时间可能会延长。您可以指示 Gradle 仅使用它已经缓存到本地的工件来避免使用网络资源

这个也是很常见的加快构建速度的方式,尤其是在国内,使用离线模式后构建速度可以大大提升。但是离线模式在每次引用新的依赖时会找不到依赖,所以新项目的话,还是能不用就不要用吧

启用按需配置

为了让 Gradle 准确了解如何构建您的应用,构建系统会在每个构建前在项目中配置所有模块以及这些模块的依赖项(即使您正在构建和测试一个模块,也是如此)。这会减慢大型多模块项目的构建进程

注意:按需配置在新版的Android Studio中已经没有了

启用并行化编译

在尝试按需配置的过程中发现Compile independent modules in parallel这个选项,查询一番后发现是使用并行化编译,能提高编译速度,勾选即可

在模块化开发中,启用并行化编译提高编译速度更为显著

创建库模块

在应用中查找您可以转换成 Android 库模块的代码。通过这种方式将您的代码模块化可以让构建系统仅编译您修改的模块,并缓存这些输出以用于未来构建。这种方式也会让按需配置和并行项目执行更有效(如果您启用这些功能)

就是让你把项目模块化,不再赘述了

为自定义构建逻辑创建任务

在您创建构建分析后,如果分析显示构建时间中相当大的一部分用在了“配置项目”阶段,请检查 build.gradle 脚本并查找您可以添加到自定义 Gradle 任务中的代码。将某个构建逻辑移动到任务中后,它仅会在需要时运行,可以为后续构建缓存结果,并且该构建逻辑将有资格并行运行(如果您启用并行项目执行)。要了解详情,请阅读官方 Gradle 文档。

就是让你把一些不是必需执行的构建转移到Task中执行,比如某个不需要在Debug时执行的构建

配置 dexOptions 和启用库预 dexing

启用这些配置可能加快构建,但是按官网说的,

您应当递增这些设置的值来试验它们并通过分析您的构建观察效果。如果您向进程分配过多的资源,性能可能会下降

所以这个还是得自己判断是否开启。至于具体的配置对应的含义,官网已经写得很清楚了,就直接发出来

  • preDexLibraries声明是否预 dex 库依赖项以加快您的增量构建速度。由于此功能可能减慢您的干净构建的速度,您可能需要为持续性集成服务器停用此功能。
  • maxProcessCount设置运行 dex-in-process 时要使用的最大线程数量。默认值为 4。
  • javaMaxHeapSize设置 DEX 编译器的最大堆大小。不过,您应当增加 Gradle 的堆大小(启用 dex-in-process 时,将与 DEX 编译器共享),而不是设置此属性。

增加 Gradle 的堆大小并启用 dex-in-process

在项目的 gradle.properties 文件中将 Gradle 的堆大小设置为 2048 MB:

org.gradle.jvmargs = -Xmx2048m

将图像转换成 WebP

这也是老生常谈了,Android Studio里就能直接转换,不过我在项目中并没有这么干

停用 PNG 处理

如果您无法(或者不想)将 PNG 图像转换成 WebP,仍可以通过在每次构建应用时停用自动图像压缩的方式加快构建速度。要停用此优化,请将以下代码添加到您的 build.gradle 文件中:

android {
  ...
  aaptOptions {
    cruncherEnabled false
  }
}

由于构建类型或产品风味不定义此属性,在构建发布版本的应用时,您需要将此属性手动设置为 true

开启了这个,好像编译速度又能快一个台阶

启用 Instant Run

前面提到过了,前提是脚本中尽量使用静态依赖。由于目前项目中动态依赖写得太多,所以这个暂时还是没有用起来

启用构建缓存

使用 Android 插件 2.3.0 及更高版本的新项目在默认情况下会启用构建缓存(除非您明确停用构建缓存)

停用注解处理器

使用注解处理器(Annotation-Processing-Tool)时,增量 Java 编译处于停用状态。如果可以,请尝试避免使用注解处理器,以便在不同构建之间仅编译您修改的类

那么多第三方库用注解写的,一般情况下不太可能停用注解处理

但是不使用Butterknife转而使用findViewById确实能提高编译速度,因为避免了生成对应的注解类。建议在子元素不多或者频繁生成时候直接findViewById

尾声

其实就是一篇官网构建速度优化的读后感,但按照提示一步步来,构建速度提升确实比较明显。下一篇会讲讲如何使用构建分析工具来分析构建速度慢的原因

Optimize your build speed

你可能感兴趣的:(Android Note - 代码构建速度优化)