首页 » Gradle Task Lazy Configuration

Gradle Task Lazy Configuration

Android 工程编译打包时间长的问题,相信许多开发者都深受其害。无论改动多小的东西,点击“运行”,要等十分钟甚至二三十分钟才能看到结果,开发效率大大降低。

其实 Google 也早已意识到了这个问题,并且一直在改进 build tools 来缓解这个问题。比如 Android Studio 2.0 的 Instant RunAndroid Studio 2.3 的 Apply Changes 和 Build Cache ,以及接下来本文要介绍的 Android Studio 3.3 的 Gradle task lazy configuration

引子: Android Gradle Plugin 更新

2019年1月发布的 Android Gradle plugin 3.3.0 更新中,插件对应的 Gradle 版本升级到了4.10.1+,插件的 Behavior changes 中包含有如下一条:

Lazy task configuration: The plugin now uses Gradle’s new task creation API to avoid initializing and configuring tasks that are not required to complete the current build (or tasks not on the execution task graph). For example, if you have multiple build variants, such as “release” and “debug” build variants, and you’re building the “debug” version of your app, the plugin avoids initializing and configuring tasks for the “release” version of your app.

由此可见,此更新来自上游 Gradle 的用来创建 task 的新 API。那么我们继续探究这个 API 有什么改进。

Gradle Task Configuration Avoidance API

Gradle 4.9 版本中,引入了新的 Task Configuration Avoidance API ,简介如下:

In a nutshell, the API allows builds to avoid the cost of creating and configuring tasks during Gradle’s configuration phase when those tasks will never be executed. For example, when running a compile task, other unrelated tasks, like code quality, testing and publishing tasks, will not be executed, so any time spent creating and configuring those tasks is unnecessary. The configuration avoidance API avoids configuring tasks if they will not be needed during the course of a build, which can have a significant impact on total configuration time.

简单来说,就是只 configure 需要被 execute 的 task,减少 build 过程中 configuration 时间。

关于 configuration 和 execution,都是属于 Gradle Build Lifecycle 的概念,我们简单来了解一下。

Gradle Build Lifecycle

官方文档解释得很清楚,这里摘录如下:

A Gradle build has three distinct phases.

Initialization
Gradle supports single and multi-project builds. During the initialization phase, Gradle determines which projects are going to take part in the build, and creates a Project instance for each of these projects.

Configuration
During this phase the project objects are configured. The build scripts of all projects which are part of the build are executed.

Execution
Gradle determines the subset of the tasks, created and configured during the configuration phase, to be executed. The subset is determined by the task name arguments passed to the gradle command and the current directory. Gradle then executes each of the selected tasks.

整个过程分为初始化配置执行三个阶段,一个 task 被执行之前需要初始化和配置。但是一个脚本中可能有很多 task ,执行某个 task 时,除了它本身和它依赖的 task 以外,其他的 task 都不会被执行。但是这些不会被执行的 task ,却也会被配置。配置这些不执行的 task 显然是不必要的,而且更致命的是这些 task 数量往往是成倍增长的,也就意味着配置的总耗时也会成倍增长。

例子

首先,Android 默认有2个 build type : debug/release ,我们再声明2个 product flavor :

flavorDimensions "version"
productFlavors {
    demo {
        dimension "version"
    }
    full {
        dimension "version"
    }
}

这样最终会产生 2 * 2 = 4 个 build variant 如下:

  • demoDebug
  • demoRelease
  • fullDebug
  • fullRelease

Android Gradle plugin 会为每个 build variant 创建相应的一系列 task(比如 assemble、check、build、clean 等等)。当我们修改完代码点击“运行”的时候,Gradle 执行的只是其中的一个 build variant(比如 demoDebug )的一系列 task ,但是所有4个 build variant 的 task 都会被配置,最坏情况下这里的配置阶段我们就浪费了75%的时间。

让我们仿照未更新前的 Android Gradle plugin 的做法,用旧的 API (task) 为每个 build variant 创建一个名为 play 的 task :

android.applicationVariants.all { variant ->
    // 旧API - 非lazy configuration
    task("play${variant.name.capitalize()}") {
        println "$name start"
        sleep(2000) // 休眠2秒,模拟耗时操作
        println "$name stop"
    }
}

这样我们就创建了如下4个 task:

  • playDemoDebug
  • playDemoRelease
  • playFullDebug
  • playFullRelease

我们运行其中的 playDemoDebug task,结果如下:

./gradlew playDemoDebug

> Configure project :app
playDemoDebug start
playDemoDebug stop
playDemoRelease start
playDemoRelease stop
playFullDebug start
playFullDebug stop
playFullRelease start
playFullRelease stop

BUILD SUCCESSFUL in 8s

可以看到,我们虽然只执行其中1个 task,但却配置了所有4个 task,每个耗时2秒,配置总耗时大约为 2 * 4 = 8 秒。

优化:新的API

我们试着改用新的 API (task.register) 来创建与之前相同的 task :

android.applicationVariants.all { variant ->
    // 新API - lazy configuration
    tasks.register("play${variant.name.capitalize()}") {
        println "$name start"
        sleep(2000)
        println "$name stop"
    }
}

再次运行其中的 playDemoDebug task ,结果如下:

./gradlew playDemoDebug
playDemoDebug start
playDemoDebug stop

BUILD SUCCESSFUL in 2s

可以看到,只配置了需要被执行的那个 task ,配置总耗时2秒。

结论

把 Android Gradle plugin 升级到 3.3 将会开启 Gradle task lazy configuration ,如果你的工程有很多 build variant 并且单个 build variant 的 configuration 耗时较长的话,那么 build 总耗时将会大幅下降。

记得自定义的 task 也要用新的 API 哟。

发表评论

您的电子邮箱地址不会被公开。 必填项已用*标注