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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,524评论 5 460
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,869评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,813评论 0 320
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,210评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,085评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,117评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,533评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,219评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,487评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,582评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,362评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,218评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,589评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,899评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,176评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,503评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,707评论 2 335

推荐阅读更多精彩内容