延迟配置任务
随着构建复杂性的增加,很难跟踪了解配置特定值的时间和位置。 Gradle 提供了多种使用惰性配置来管理此问题的方法。
了解惰性属性
Gradle 提供了惰性属性,它会延迟计算属性的值,直到实际需要时才计算。
惰性属性提供了三个主要好处:
-
延迟值解析:允许连接 Gradle 模型,而无需知道属性值何时已知。例如,您可能希望根据扩展的源目录属性设置任务的输入源文件,但在构建脚本或其他插件配置它们之前,扩展属性值是未知的。
-
自动任务依赖性管理:将一个任务的输出连接到另一任务的输入,自动确定任务依赖性。属性实例携带有关哪个任务(如果有)产生其值的信息。构建作者无需担心任务依赖关系与配置更改保持同步。
-
改进的构建性能:避免配置期间的资源密集型工作,从而对构建性能产生积极影响。例如,当配置值来自解析文件但仅在运行功能测试时使用时,使用属性实例捕获该值意味着仅在运行功能测试时(而不是
clean
运行时)解析文件例子)。
Gradle 用两个接口表示惰性属性:
- 提供者
-
代表一个值,只能查询,不能更改。
-
这些类型的属性是只读的。
-
Provider.get()方法返回属性的当前值。
-
可以使用Provider.map(Transformer)从另一个
Provider
创建A。Provider
-
许多其他类型都可以扩展并可以在任何需要的
Provider
地方使用。Provider
-
- 财产
-
表示一个可以查询和更改的值。
-
这些类型的属性是可配置的。
-
Property
扩展Provider
接口。 -
Property.set(T)方法指定属性的值,覆盖可能存在的任何值。
-
Property.set(Provider)方法指定
Provider
属性的值,覆盖可能存在的任何值。这允许您在配置值之前Provider
将实例连接在一起。Property
-
A可以通过工厂方法ObjectFactory.property(Class)
Property
创建。
-
惰性属性旨在传递并仅在需要时进行查询。这通常发生在执行阶段。
下面演示了一个具有可配置greeting
属性和只读message
属性的任务:
abstract class Greeting : DefaultTask() { (1)
@get:Input
abstract val greeting: Property<String> (2)
@Internal
val message: Provider<String> = greeting.map { it + " from Gradle" } (3)
@TaskAction
fun printMessage() {
logger.quiet(message.get())
}
}
tasks.register<Greeting>("greeting") {
greeting.set("Hi") (4)
greeting = "Hi" (5)
}
abstract class Greeting extends DefaultTask { (1)
@Input
abstract Property<String> getGreeting() (2)
@Internal
final Provider<String> message = greeting.map { it + ' from Gradle' } (3)
@TaskAction
void printMessage() {
logger.quiet(message.get())
}
}
tasks.register("greeting", Greeting) {
greeting.set('Hi') (4)
greeting = 'Hi' (5)
}
1 | 显示问候语的任务 |
2 | 可配置的问候语 |
3 | 根据问候语计算的只读属性 |
4 | 配置问候语 |
5 | 调用 Property.set() 的替代表示法 |
$ gradle greeting > Task :greeting Hi from Gradle BUILD SUCCESSFUL in 0s 1 actionable task: 1 executed
该Greeting
任务具有表示可配置问候语的 type 属性和表示计算的只读消息的Property<String>
type 属性。Provider<String>
该消息是使用以下方法Provider
从问候语创建的;当问候语属性的值发生变化时,它的值会保持最新。Property
map()
创建 Property 或 Provider 实例
Provider
它及其子类型(例如)都不Property
打算由构建脚本或插件实现。 Gradle 提供了工厂方法来创建这些类型的实例。
在前面的示例中,提供了两个工厂方法:
-
ObjectFactory.property(Class)创建一个新
Property
实例。可以从Project.getObjects()引用或通过构造函数或方法注入来引用ObjectFactory的实例。ObjectFactory
-
Provider.map(Transformer)
Provider
从现有的Provider
或实例创建一个新的Property
。
请参阅快速参考了解所有可用的类型和工厂。
A也可以通过工厂方法ProviderFactory.provider(Callable)Provider
创建。
没有使用 当使用 Groovy 编写插件或构建脚本时,您可以使用 同样,当使用 Kotlin 编写插件或构建脚本时,Kotlin 编译器会将 Kotlin 函数转换为 |
将属性连接在一起
惰性属性的一个重要特征是它们可以连接在一起,以便对一个属性的更改自动反映在其他属性中。
下面是一个示例,其中任务的属性连接到项目扩展的属性:
// A project extension
interface MessageExtension {
// A configurable greeting
abstract val greeting: Property<String>
}
// A task that displays a greeting
abstract class Greeting : DefaultTask() {
// Configurable by the user
@get:Input
abstract val greeting: Property<String>
// Read-only property calculated from the greeting
@Internal
val message: Provider<String> = greeting.map { it + " from Gradle" }
@TaskAction
fun printMessage() {
logger.quiet(message.get())
}
}
// Create the project extension
val messages = project.extensions.create<MessageExtension>("messages")
// Create the greeting task
tasks.register<Greeting>("greeting") {
// Attach the greeting from the project extension
// Note that the values of the project extension have not been configured yet
greeting = messages.greeting
}
messages.apply {
// Configure the greeting on the extension
// Note that there is no need to reconfigure the task's `greeting` property. This is automatically updated as the extension property changes
greeting = "Hi"
}
// A project extension
interface MessageExtension {
// A configurable greeting
Property<String> getGreeting()
}
// A task that displays a greeting
abstract class Greeting extends DefaultTask {
// Configurable by the user
@Input
abstract Property<String> getGreeting()
// Read-only property calculated from the greeting
@Internal
final Provider<String> message = greeting.map { it + ' from Gradle' }
@TaskAction
void printMessage() {
logger.quiet(message.get())
}
}
// Create the project extension
project.extensions.create('messages', MessageExtension)
// Create the greeting task
tasks.register("greeting", Greeting) {
// Attach the greeting from the project extension
// Note that the values of the project extension have not been configured yet
greeting = messages.greeting
}
messages {
// Configure the greeting on the extension
// Note that there is no need to reconfigure the task's `greeting` property. This is automatically updated as the extension property changes
greeting = 'Hi'
}
$ gradle greeting > Task :greeting Hi from Gradle BUILD SUCCESSFUL in 0s 1 actionable task: 1 executed
此示例调用Property.set(Provider)方法将 a 附加Provider
到 aProperty
以提供属性的值。在这种情况下,Provider
恰好Property
也是 a,但您可以连接任何Provider
实现,例如使用创建的实现Provider.map()
处理文件
在使用文件中,我们为File
类对象引入了四种集合类型:
只读类型 | 可配置类型 |
---|---|
所有这些类型也被视为惰性类型。
有更强类型的模型用于表示文件系统的元素: Directory和RegularFile。这些类型不应与标准 Java文件类型混淆,因为它们用于告诉 Gradle 您需要更具体的值,例如目录或非目录、常规文件。
Gradle 提供了两个专门的Property
子类型来处理这些类型的值:
RegularFileProperty和DirectoryProperty。ObjectFactory有方法来创建这些:ObjectFactory.fileProperty()和ObjectFactory.directoryProperty()。
ADirectoryProperty
还可用于分别通过DirectoryProperty.dir(String)和DirectoryProperty.file(String)Provider
创建对 a进行延迟计算的Directory
值。这些方法创建的提供程序的值是相对于它们的创建位置来计算的。从这些提供程序返回的值将反映对.RegularFile
DirectoryProperty
DirectoryProperty
// A task that generates a source file and writes the result to an output directory
abstract class GenerateSource : DefaultTask() {
// The configuration file to use to generate the source file
@get:InputFile
abstract val configFile: RegularFileProperty
// The directory to write source files to
@get:OutputDirectory
abstract val outputDir: DirectoryProperty
@TaskAction
fun compile() {
val inFile = configFile.get().asFile
logger.quiet("configuration file = $inFile")
val dir = outputDir.get().asFile
logger.quiet("output dir = $dir")
val className = inFile.readText().trim()
val srcFile = File(dir, "${className}.java")
srcFile.writeText("public class ${className} { }")
}
}
// Create the source generation task
tasks.register<GenerateSource>("generate") {
// Configure the locations, relative to the project and build directories
configFile = layout.projectDirectory.file("src/config.txt")
outputDir = layout.buildDirectory.dir("generated-source")
}
// Change the build directory
// Don't need to reconfigure the task properties. These are automatically updated as the build directory changes
layout.buildDirectory = layout.projectDirectory.dir("output")
// A task that generates a source file and writes the result to an output directory
abstract class GenerateSource extends DefaultTask {
// The configuration file to use to generate the source file
@InputFile
abstract RegularFileProperty getConfigFile()
// The directory to write source files to
@OutputDirectory
abstract DirectoryProperty getOutputDir()
@TaskAction
def compile() {
def inFile = configFile.get().asFile
logger.quiet("configuration file = $inFile")
def dir = outputDir.get().asFile
logger.quiet("output dir = $dir")
def className = inFile.text.trim()
def srcFile = new File(dir, "${className}.java")
srcFile.text = "public class ${className} { ... }"
}
}
// Create the source generation task
tasks.register('generate', GenerateSource) {
// Configure the locations, relative to the project and build directories
configFile = layout.projectDirectory.file('src/config.txt')
outputDir = layout.buildDirectory.dir('generated-source')
}
// Change the build directory
// Don't need to reconfigure the task properties. These are automatically updated as the build directory changes
layout.buildDirectory = layout.projectDirectory.dir('output')
$ gradle generate > Task :generate configuration file = /home/user/gradle/samples/src/config.txt output dir = /home/user/gradle/samples/output/generated-source BUILD SUCCESSFUL in 0s 1 actionable task: 1 executed
$ gradle generate > Task :generate configuration file = /home/user/gradle/samples/kotlin/src/config.txt output dir = /home/user/gradle/samples/kotlin/output/generated-source BUILD SUCCESSFUL in 0s 1 actionable task: 1 executed
此示例通过Project.getLayout()以及ProjectLayout.getBuildDirectory()和ProjectLayout.getProjectDirectory()创建表示项目中位置和构建目录的提供程序。
要关闭循环,请注意 aDirectoryProperty
或简单的Directory
可以转换为 a ,允许使用DirectoryProperty.getAsFileTree()或Directory.getAsFileTree()FileTree
查询目录中包含的文件和目录。从 a或 a ,您可以使用DirectoryProperty.files(Object...)或Directory.files(Object...)创建包含目录中包含的一组文件的实例。DirectoryProperty
Directory
FileCollection
使用任务输入和输出
许多构建都有多个连接在一起的任务,其中一个任务将另一个任务的输出用作输入。
为了实现这项工作,我们需要配置每个任务,以了解在哪里查找其输入以及在哪里放置其输出。确保生产任务和消费任务配置在相同的位置,并在任务之间附加任务依赖关系。如果这些值中的任何一个可由用户配置或由多个插件配置,则这可能会很麻烦且脆弱,因为任务属性需要以正确的顺序和位置进行配置,并且任务依赖项在值更改时保持同步。
APIProperty
通过跟踪属性的值和生成该值的任务使这变得更容易。
作为示例,请考虑以下带有连接在一起的生产者和消费者任务的插件:
abstract class Producer : DefaultTask() {
@get:OutputFile
abstract val outputFile: RegularFileProperty
@TaskAction
fun produce() {
val message = "Hello, World!"
val output = outputFile.get().asFile
output.writeText( message)
logger.quiet("Wrote '${message}' to ${output}")
}
}
abstract class Consumer : DefaultTask() {
@get:InputFile
abstract val inputFile: RegularFileProperty
@TaskAction
fun consume() {
val input = inputFile.get().asFile
val message = input.readText()
logger.quiet("Read '${message}' from ${input}")
}
}
val producer = tasks.register<Producer>("producer")
val consumer = tasks.register<Consumer>("consumer")
consumer {
// Connect the producer task output to the consumer task input
// Don't need to add a task dependency to the consumer task. This is automatically added
inputFile = producer.flatMap { it.outputFile }
}
producer {
// Set values for the producer lazily
// Don't need to update the consumer.inputFile property. This is automatically updated as producer.outputFile changes
outputFile = layout.buildDirectory.file("file.txt")
}
// Change the build directory.
// Don't need to update producer.outputFile and consumer.inputFile. These are automatically updated as the build directory changes
layout.buildDirectory = layout.projectDirectory.dir("output")
abstract class Producer extends DefaultTask {
@OutputFile
abstract RegularFileProperty getOutputFile()
@TaskAction
void produce() {
String message = 'Hello, World!'
def output = outputFile.get().asFile
output.text = message
logger.quiet("Wrote '${message}' to ${output}")
}
}
abstract class Consumer extends DefaultTask {
@InputFile
abstract RegularFileProperty getInputFile()
@TaskAction
void consume() {
def input = inputFile.get().asFile
def message = input.text
logger.quiet("Read '${message}' from ${input}")
}
}
def producer = tasks.register("producer", Producer)
def consumer = tasks.register("consumer", Consumer)
consumer.configure {
// Connect the producer task output to the consumer task input
// Don't need to add a task dependency to the consumer task. This is automatically added
inputFile = producer.flatMap { it.outputFile }
}
producer.configure {
// Set values for the producer lazily
// Don't need to update the consumer.inputFile property. This is automatically updated as producer.outputFile changes
outputFile = layout.buildDirectory.file('file.txt')
}
// Change the build directory.
// Don't need to update producer.outputFile and consumer.inputFile. These are automatically updated as the build directory changes
layout.buildDirectory = layout.projectDirectory.dir('output')
$ gradle consumer > Task :producer Wrote 'Hello, World!' to /home/user/gradle/samples/output/file.txt > Task :consumer Read 'Hello, World!' from /home/user/gradle/samples/output/file.txt BUILD SUCCESSFUL in 0s 2 actionable tasks: 2 executed
$ gradle consumer > Task :producer Wrote 'Hello, World!' to /home/user/gradle/samples/kotlin/output/file.txt > Task :consumer Read 'Hello, World!' from /home/user/gradle/samples/kotlin/output/file.txt BUILD SUCCESSFUL in 0s 2 actionable tasks: 2 executed
在上面的示例中,任务输出和输入在定义任何位置之前就已连接。 setter 可以在任务执行之前随时调用,并且更改将自动影响所有相关的输入和输出属性。
此示例中需要注意的另一个重要事项是不存在任何显式任务依赖性。使用Providers
跟踪哪个任务产生其值来表示任务输出,并将它们用作任务输入将隐式添加正确的任务依赖项。
隐式任务依赖项也适用于非文件的输入属性:
abstract class Producer : DefaultTask() {
@get:OutputFile
abstract val outputFile: RegularFileProperty
@TaskAction
fun produce() {
val message = "Hello, World!"
val output = outputFile.get().asFile
output.writeText( message)
logger.quiet("Wrote '${message}' to ${output}")
}
}
abstract class Consumer : DefaultTask() {
@get:Input
abstract val message: Property<String>
@TaskAction
fun consume() {
logger.quiet(message.get())
}
}
val producer = tasks.register<Producer>("producer") {
// Set values for the producer lazily
// Don't need to update the consumer.inputFile property. This is automatically updated as producer.outputFile changes
outputFile = layout.buildDirectory.file("file.txt")
}
tasks.register<Consumer>("consumer") {
// Connect the producer task output to the consumer task input
// Don't need to add a task dependency to the consumer task. This is automatically added
message = producer.flatMap { it.outputFile }.map { it.asFile.readText() }
}
abstract class Producer extends DefaultTask {
@OutputFile
abstract RegularFileProperty getOutputFile()
@TaskAction
void produce() {
String message = 'Hello, World!'
def output = outputFile.get().asFile
output.text = message
logger.quiet("Wrote '${message}' to ${output}")
}
}
abstract class Consumer extends DefaultTask {
@Input
abstract Property<String> getMessage()
@TaskAction
void consume() {
logger.quiet(message.get())
}
}
def producer = tasks.register('producer', Producer) {
// Set values for the producer lazily
// Don't need to update the consumer.inputFile property. This is automatically updated as producer.outputFile changes
outputFile = layout.buildDirectory.file('file.txt')
}
tasks.register('consumer', Consumer) {
// Connect the producer task output to the consumer task input
// Don't need to add a task dependency to the consumer task. This is automatically added
message = producer.flatMap { it.outputFile }.map { it.asFile.text }
}
$ gradle consumer > Task :producer Wrote 'Hello, World!' to /home/user/gradle/samples/build/file.txt > Task :consumer Hello, World! BUILD SUCCESSFUL in 0s 2 actionable tasks: 2 executed
$ gradle consumer > Task :producer Wrote 'Hello, World!' to /home/user/gradle/samples/kotlin/build/file.txt > Task :consumer Hello, World! BUILD SUCCESSFUL in 0s 2 actionable tasks: 2 executed
使用集合
Gradle 提供了两种惰性属性类型来帮助配置Collection
属性。
它们的工作方式与任何其他提供程序完全相同Provider
,并且就像文件提供程序一样,它们有额外的建模:
-
对于
List
值,该接口称为ListProperty。
您可以ListProperty
使用ObjectFactory.listProperty(Class)并指定元素类型来创建新的。 -
对于
Set
值,该接口称为SetProperty。
您可以SetProperty
使用ObjectFactory.setProperty(Class)并指定元素类型来创建新的。
这种类型的属性允许您使用HasMultipleValues.set(Iterable)和HasMultipleValues.set(Provider)覆盖整个集合值,或者通过各种add
方法添加新元素:
-
HasMultipleValues.add(T) : 将单个元素添加到集合中
-
HasMultipleValues.add(Provider):将延迟计算的元素添加到集合中
-
HasMultipleValues.addAll(Provider):将延迟计算的元素集合添加到列表中
就像 every 一样Provider
,集合是在调用Provider.get()时计算的。以下示例显示了ListProperty 的实际操作:
abstract class Producer : DefaultTask() {
@get:OutputFile
abstract val outputFile: RegularFileProperty
@TaskAction
fun produce() {
val message = "Hello, World!"
val output = outputFile.get().asFile
output.writeText( message)
logger.quiet("Wrote '${message}' to ${output}")
}
}
abstract class Consumer : DefaultTask() {
@get:InputFiles
abstract val inputFiles: ListProperty<RegularFile>
@TaskAction
fun consume() {
inputFiles.get().forEach { inputFile ->
val input = inputFile.asFile
val message = input.readText()
logger.quiet("Read '${message}' from ${input}")
}
}
}
val producerOne = tasks.register<Producer>("producerOne")
val producerTwo = tasks.register<Producer>("producerTwo")
tasks.register<Consumer>("consumer") {
// Connect the producer task outputs to the consumer task input
// Don't need to add task dependencies to the consumer task. These are automatically added
inputFiles.add(producerOne.get().outputFile)
inputFiles.add(producerTwo.get().outputFile)
}
// Set values for the producer tasks lazily
// Don't need to update the consumer.inputFiles property. This is automatically updated as producer.outputFile changes
producerOne { outputFile = layout.buildDirectory.file("one.txt") }
producerTwo { outputFile = layout.buildDirectory.file("two.txt") }
// Change the build directory.
// Don't need to update the task properties. These are automatically updated as the build directory changes
layout.buildDirectory = layout.projectDirectory.dir("output")
abstract class Producer extends DefaultTask {
@OutputFile
abstract RegularFileProperty getOutputFile()
@TaskAction
void produce() {
String message = 'Hello, World!'
def output = outputFile.get().asFile
output.text = message
logger.quiet("Wrote '${message}' to ${output}")
}
}
abstract class Consumer extends DefaultTask {
@InputFiles
abstract ListProperty<RegularFile> getInputFiles()
@TaskAction
void consume() {
inputFiles.get().each { inputFile ->
def input = inputFile.asFile
def message = input.text
logger.quiet("Read '${message}' from ${input}")
}
}
}
def producerOne = tasks.register('producerOne', Producer)
def producerTwo = tasks.register('producerTwo', Producer)
tasks.register('consumer', Consumer) {
// Connect the producer task outputs to the consumer task input
// Don't need to add task dependencies to the consumer task. These are automatically added
inputFiles.add(producerOne.get().outputFile)
inputFiles.add(producerTwo.get().outputFile)
}
// Set values for the producer tasks lazily
// Don't need to update the consumer.inputFiles property. This is automatically updated as producer.outputFile changes
producerOne.configure { outputFile = layout.buildDirectory.file('one.txt') }
producerTwo.configure { outputFile = layout.buildDirectory.file('two.txt') }
// Change the build directory.
// Don't need to update the task properties. These are automatically updated as the build directory changes
layout.buildDirectory = layout.projectDirectory.dir('output')
$ gradle consumer > Task :producerOne Wrote 'Hello, World!' to /home/user/gradle/samples/output/one.txt > Task :producerTwo Wrote 'Hello, World!' to /home/user/gradle/samples/output/two.txt > Task :consumer Read 'Hello, World!' from /home/user/gradle/samples/output/one.txt Read 'Hello, World!' from /home/user/gradle/samples/output/two.txt BUILD SUCCESSFUL in 0s 3 actionable tasks: 3 executed
$ gradle consumer > Task :producerOne Wrote 'Hello, World!' to /home/user/gradle/samples/kotlin/output/one.txt > Task :producerTwo Wrote 'Hello, World!' to /home/user/gradle/samples/kotlin/output/two.txt > Task :consumer Read 'Hello, World!' from /home/user/gradle/samples/kotlin/output/one.txt Read 'Hello, World!' from /home/user/gradle/samples/kotlin/output/two.txt BUILD SUCCESSFUL in 0s 3 actionable tasks: 3 executed
使用地图
Gradle 提供了一个惰性MapProperty类型来允许Map
配置值。您可以MapProperty
使用ObjectFactory.mapProperty(Class, Class)创建实例。
与其他属性类型类似,aMapProperty
有一个set()方法,可用于指定属性的值。一些附加方法允许将具有惰性值的条目添加到映射中。
abstract class Generator: DefaultTask() {
@get:Input
abstract val properties: MapProperty<String, Int>
@TaskAction
fun generate() {
properties.get().forEach { entry ->
logger.quiet("${entry.key} = ${entry.value}")
}
}
}
// Some values to be configured later
var b = 0
var c = 0
tasks.register<Generator>("generate") {
properties.put("a", 1)
// Values have not been configured yet
properties.put("b", providers.provider { b })
properties.putAll(providers.provider { mapOf("c" to c, "d" to c + 1) })
}
// Configure the values. There is no need to reconfigure the task
b = 2
c = 3
abstract class Generator extends DefaultTask {
@Input
abstract MapProperty<String, Integer> getProperties()
@TaskAction
void generate() {
properties.get().each { key, value ->
logger.quiet("${key} = ${value}")
}
}
}
// Some values to be configured later
def b = 0
def c = 0
tasks.register('generate', Generator) {
properties.put("a", 1)
// Values have not been configured yet
properties.put("b", providers.provider { b })
properties.putAll(providers.provider { [c: c, d: c + 1] })
}
// Configure the values. There is no need to reconfigure the task
b = 2
c = 3
$ gradle generate > Task :generate a = 1 b = 2 c = 3 d = 4 BUILD SUCCESSFUL in 0s 1 actionable task: 1 executed
将约定应用于属性
通常,您希望对要在未配置值的情况下使用的属性应用一些约定或默认值。您可以使用该convention()
方法来实现此目的。此方法接受一个值或 a Provider
,并且这将用作该值,直到配置其他值。
tasks.register("show") {
val property = objects.property(String::class)
// Set a convention
property.convention("convention 1")
println("value = " + property.get())
// Can replace the convention
property.convention("convention 2")
println("value = " + property.get())
property.set("explicit value")
// Once a value is set, the convention is ignored
property.convention("ignored convention")
doLast {
println("value = " + property.get())
}
}
tasks.register("show") {
def property = objects.property(String)
// Set a convention
property.convention("convention 1")
println("value = " + property.get())
// Can replace the convention
property.convention("convention 2")
println("value = " + property.get())
property.set("explicit value")
// Once a value is set, the convention is ignored
property.convention("ignored convention")
doLast {
println("value = " + property.get())
}
}
$ gradle show value = convention 1 value = convention 2 > Task :show value = explicit value BUILD SUCCESSFUL in 0s 1 actionable task: 1 executed
使属性不可修改
任务或项目的大多数属性旨在通过插件或构建脚本进行配置,以便它们可以为该构建使用特定值。
例如,指定编译任务的输出目录的属性可能以插件指定的值开头。然后构建脚本可能会将该值更改为某个自定义位置,然后任务运行时将使用该值。但是,一旦任务开始运行,我们希望防止进一步的属性更改。通过这种方式,我们可以避免因不同的使用者(例如任务操作、Gradle 的最新检查、构建缓存或其他任务)使用不同的属性值而导致的错误。
惰性属性提供了多种方法,您可以使用这些方法在配置值后禁止更改其值。 FinalizeValue ()方法计算属性的最终值并防止对该属性进行进一步更改。
libVersioning.version.finalizeValue()
当属性的值来自 a 时Provider
,将向提供者查询其当前值,结果将成为该属性的最终值。此最终值取代了提供者,并且该属性不再跟踪提供者的值。调用此方法还会使属性实例不可修改,并且任何进一步更改该属性值的尝试都将失败。当任务开始执行时,Gradle 会自动将任务的属性设为最终属性。
FinalizeValueOnRead()方法类似,只不过在查询属性值之前不会计算属性的最终值。
modifiedFiles.finalizeValueOnRead()
换句话说,该方法根据需要延迟计算最终值,而finalizeValue()
急切地计算最终值。当该值的计算成本可能很高或尚未配置时,可以使用此方法。您还希望确保该属性的所有使用者在查询该值时看到相同的值。
使用提供商 API
成功使用 Provider API 的指南:
-
Property和Provider类型具有查询或配置值所需的所有重载。因此,您应该遵循以下准则:
-
对于可配置属性,直接通过单个 getter公开属性。
-
对于不可配置的属性,直接通过单个 getter公开Provider 。
-
-
避免通过引入额外的 getter 和 setter 来简化代码中的调用,例如
obj.getProperty().get()
和。obj.getProperty().set(T)
-
将插件迁移到使用提供程序时,请遵循以下准则:
惰性对象 API 参考
将这些类型用于只读值:
- 提供者<T>
-
其值为以下实例的属性
T
- 工厂
-
-
ProviderFactory.provider(Callable)。始终优先选择其他工厂方法之一而不是此方法。
将这些类型用于可变值:
- 属性<T>
-
其值为以下实例的属性
T