美文网首页
Gradle Task Lazy Configuration

Gradle Task Lazy Configuration

作者: CrazyOrr | 来源:发表于2020-03-03 13:40 被阅读0次

    引子

    在Gradle脚本中如下遍历 build variant 的依赖classpath:

    android.libraryVariants.each { variant ->
        if (variant.name.endsWith("release")) {
            androidJavadoc {
                classpath += variant.javaCompile.classpath
            }
        }
    }
    

    这里的variant.javaCompile调用会引发如下的警告:

    INFO: API 'variant.getJavaCompile()' is obsolete and has been replaced with 'variant.getJavaCompileProvider()'.
    It will be removed at the end of 2019.
    For more information, see https://d.android.com/r/tools/task-configuration-avoidance.
    To determine what is calling variant.getJavaCompile(), use -Pandroid.debug.obsoleteApi=true on the command line to display more information.
    Affected Modules: library
    

    这个警告,大家在写Gradle脚本时可能都见过。现在已经2020年了,也就是说Gradle官方已经只支持新的方式了,那么,这到底意味着什么?我们又需要做什么呢?

    警告来源

    Android中引入这个改动的是Android Gradle plugin 3.3.0 (2019年1月),此版本插件对应的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.
    
    Calling certain older methods in the Variants API, such as variant.getJavaCompile(), might still force task configuration. To make sure that your build is optimized for lazy task configuration, invoke new methods that instead return a TaskProvider object, such as variant.getJavaCompileProvider().
    
    If you execute custom build tasks, learn how to adapt to Gradle’s new task-creation API.
    

    由此我们了解到在Gradle 4.9版本中,引入了新的Task Configuration Avoidance API

    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.
    

    示例

    让我们来看具体的例子:

    首先,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 生成一个名为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

    注意,创建Task时我们使用的是旧的API,也就是非"lazy"的,我们execute其中的1个时,所有4个都会被configure。
    运行验证一下:

    ./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
    

    可以看到,在Configuration阶段,4个Task的configure逻辑都被执行了。
    为了让结果更明显,我们为configure设置了2秒的延时,而其他环节没有执行任何操作,耗时几乎为0。
    build的总耗时8秒,约等于单次Task configuration的耗时(2秒) * Task数量(4),符合预期。

    然后我们改用新的API来创建Task:

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

    此时Task的configuration应该是"lazy"的,也就是说只有被execute的Task才会被configure。
    再次运行验证一下:

    ./gradlew playDemoDebug
    playDemoDebug start
    playDemoDebug stop
    
    BUILD SUCCESSFUL in 2s
    

    可以看到,确实只configure了被execute的 playDemoDebug Task,build的总耗时也相应下降到了约为单次Task configuration的耗时的2秒,符合预期。

    意义

    可以明显看到,在 build variant 较多且configuration耗时较长的情况下,这个优化会极大的减少build单个variant所需要的时间。

    结论

    1. 保持AGP/Gradle的更新,对开发效率提升巨大。
    2. 如果自己编写Gradle脚本或插件时有关于创建task的,应该尽快迁移使用新的API。

    相关文章

      网友评论

          本文标题:Gradle Task Lazy Configuration

          本文链接:https://www.haomeiwen.com/subject/jyuclhtx.html