Skip to content

延迟属性 (Lazy API)

源:Gradle 官方文档 - Lazy Configuration

Lazy API 通过延迟计算和配置,避免在配置阶段执行耗时操作,是现代 Gradle 推荐的配置方式。

Lazy API 概念

为什么需要 Lazy API

传统方式的问题

kotlin
// ❌ 配置阶段立即执行
val version = "git rev-parse HEAD".execute().text

问题

  • 配置阶段执行命令
  • 影响配置性能
  • 破坏配置缓存
  • 每次同步都执行

Lazy API 的优势

kotlin
// ✅ 延迟到需要时才执行
val version = providers.exec {
    commandLine("git", "rev-parse", "HEAD")
}.standardOutput.asText

优势

  • 配置阶段不执行
  • 支持配置缓存
  • 按需计算
  • 性能更好

Provider API

Provider<T>

基本概念

Provider<T> 是一个延迟计算的值容器。

kotlin
val provider: Provider<String> = providers.provider {
    "computed value"
}

// 获取值
val value = provider.get()

创建 Provider

使用 provider {}

kotlin
val myProvider = providers.provider {
    "Hello, ${project.name}"
}

从属性创建

kotlin
val version = providers.gradleProperty("app.version")
val env = providers.environmentVariable("BUILD_ENV")

从系统属性

kotlin
val javaHome = providers.systemProperty("java.home")

Property API

Property<T>

基本概念

Property<T> 是可变的 Provider<T>

kotlin
abstract class MyTask : DefaultTask() {
    @get:Input
    abstract val message: Property<String>
    
    @TaskAction
    fun execute() {
        println(message.get())
    }
}

tasks.register<MyTask>("myTask") {
    message.set("Hello")
}

类型化 Property

RegularFileProperty

kotlin
abstract class ProcessTask : DefaultTask() {
    @get:InputFile
    abstract val inputFile: RegularFileProperty
    
    @TaskAction
    fun process() {
        val file = inputFile.get().asFile
        println(file.readText())
    }
}

tasks.register<ProcessTask>("process") {
    inputFile.set(file("input.txt"))
}

DirectoryProperty

kotlin
abstract val outputDir: DirectoryProperty

tasks.register<MyTask>("generate") {
    outputDir.set(layout.buildDirectory.dir("output"))
}

ListProperty

kotlin
abstract val items: ListProperty<String>

tasks.register<MyTask>("process") {
    items.set(listOf("a", "b", "c"))
    items.add("d")
}

MapProperty

kotlin
abstract val config: MapProperty<String, String>

tasks.register<MyTask>("configure") {
    config.put("key", "value")
}

延迟配置

延迟设置

kotlin
val version = providers.gradleProperty("app.version")

tasks.register<MyTask>("printVersion") {
    // 延迟设置,不会立即获取值
    versionName.set(version)
}

默认值

kotlin
val port = providers.gradleProperty("server.port")
    .orElse("8080")

tasks.register<ServerTask>("startServer") {
    serverPort.set(port)
}

连接 Provider

map 映射

kotlin
val version = providers.gradleProperty("version")
val fullName = version.map { "v$it" }

tasks.register<MyTask>("printVersion") {
    versionName.set(fullName)
}

flatMap

kotlin
val baseDir = layout.projectDirectory
val configFile = baseDir.file("config.properties")

val config = configFile.flatMap { file ->
    providers.fileContents(file).asText
}

zip 组合

kotlin
val major = providers.gradleProperty("version.major")
val minor = providers.gradleProperty("version.minor")

val version = major.zip(minor) { maj, min ->
    "$maj.$min"
}

gradle.properties 集成

读取属性

kotlin
// gradle.properties
// app.version=1.0.0
// server.port=8080

val appVersion = providers.gradleProperty("app.version")
val serverPort = providers.gradleProperty("server.port")
    .map { it.toInt() }

带默认值

kotlin
val timeout = providers.gradleProperty("http.timeout")
    .orElse("30")
    .map { it.toInt() }

执行命令

exec Provider

kotlin
val gitHash = providers.exec {
    commandLine("git", "rev-parse", "--short", "HEAD")
}.standardOutput.asText.map { it.trim() }

tasks.register<GenerateTask>("generate") {
    hash.set(gitHash)
}

处理输出

kotlin
val gitBranch = providers.exec {
    commandLine("git", "branch", "--show-current")
}.standardOutput.asText.map { it.trim() }

val buildInfo = gitBranch.map { branch ->
    "Branch: $branch, Time: ${System.currentTimeMillis()}"
}

文件内容

读取文件

kotlin
val configFile = layout.projectDirectory.file("config.txt")
val content = providers.fileContents(configFile).asText

tasks.register<MyTask>("process") {
    config.set(content)
}

任务输入输出

连接任务输出

kotlin
abstract class ProducerTask : DefaultTask() {
    @get:OutputFile
    abstract val outputFile: RegularFileProperty
    
    @TaskAction
    fun produce() {
        outputFile.get().asFile.writeText("data")
    }
}

abstract class ConsumerTask : DefaultTask() {
    @get:InputFile
    abstract val inputFile: RegularFileProperty
    
    @TaskAction
    fun consume() {
        println(inputFile.get().asFile.readText())
    }
}

val producer = tasks.register<ProducerTask>("producer") {
    outputFile.set(layout.buildDirectory.file("data.txt"))
}

tasks.register<ConsumerTask>("consumer") {
    // 自动建立依赖关系
    inputFile.set(producer.flatMap { it.outputFile })
}

实战案例

案例1:版本信息

kotlin
val gitHash = providers.exec {
    commandLine("git", "rev-parse", "--short", "HEAD")
}.standardOutput.asText.map { it.trim() }

val buildTime = providers.provider {
    java.time.Instant.now().toString()
}

val versionInfo = gitHash.zip(buildTime) { hash, time ->
    "Hash: $hash, Time: $time"
}

tasks.register("printVersion") {
    doLast {
        println(versionInfo.get())
    }
}

案例2:环境配置

kotlin
val env = providers.environmentVariable("BUILD_ENV")
    .orElse("dev")

val apiUrl = env.map { environment ->
    when (environment) {
        "prod" -> "https://api.example.com"
        "staging" -> "https://staging-api.example.com"
        else -> "https://dev-api.example.com"
    }
}

android {
    defaultConfig {
        buildConfigField("String", "API_URL", "\"${apiUrl.get()}\"")
    }
}

案例3:动态资源

kotlin
val locale = providers.gradleProperty("app.locale")
    .orElse("en")

val resourceDir = locale.map { loc ->
    layout.projectDirectory.dir("src/main/res-$loc")
}

android {
    sourceSets {
        named("main") {
            res.srcDir(resourceDir)
        }
    }
}

与传统 API 对比

传统方式

kotlin
// ❌ 配置阶段立即执行
val gitHash = "git rev-parse HEAD".execute().text

tasks.register("printHash") {
    doLast {
        println(gitHash)
    }
}

Lazy API 方式

kotlin
// ✅ 延迟执行
val gitHash = providers.exec {
    commandLine("git", "rev-parse", "HEAD")
}.standardOutput.asText

tasks.register("printHash") {
    doLast {
        println(gitHash.get())
    }
}

对比

特性传统方式Lazy API
执行时机配置阶段按需执行
配置缓存
性能
推荐度

最佳实践

使用抽象属性

kotlin
abstract class MyTask : DefaultTask() {
    @get:Input
    abstract val prop: Property<String>
}

提供默认值

kotlin
val timeout = providers.gradleProperty("timeout")
    .orElse("30")

链式操作

kotlin
val result = provider1
    .map { transform1(it) }
    .flatMap { provider2 }
    .map { transform2(it) }

避免 get() 在配置阶段

kotlin
// ❌ 配置阶段调用 get()
val value = myProvider.get()

// ✅ 传递 Provider
someProperty.set(myProvider)

使用 finalizeValue()

kotlin
val prop = objects.property<String>()
prop.set("initial")
prop.finalizeValue()
// prop.set("changed")  // 错误!已finalize

使用 disallowChanges()

kotlin
val prop = objects.property<String>()
prop.set("value")
prop.disallowChanges()