延迟属性 (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()