隨著建置複雜度的增加,很難追蹤特定值何時何地設定。Gradle 提供多種方法來使用延遲設定管理這個問題。

了解 Lazy 屬性
Gradle 提供延遲屬性,會延遲計算屬性值,直到實際需要為止。
延遲屬性提供三個主要好處
-
延遲值解析:允許連接 Gradle 模型,而無需知道屬性值何時會知道。例如,您可能想根據擴充功能的來源目錄屬性設定工作輸入來源檔案,但擴充功能屬性值在建置指令碼或其他外掛設定它們之前並不知道。
-
自動工作相依性管理:將一個工作的輸出連接到另一個工作的輸入,自動確定工作相依性。屬性執行個體會攜帶有關哪個工作(如果有)產生其值資訊。建置作者無需擔心在設定變更時保持工作相依性同步。
-
改善建置效能:避免在設定期間進行資源密集的工作,對建置效能產生正面影響。例如,當設定值來自剖析檔案,但僅在執行功能測試時使用,使用屬性實例擷取此值表示僅在執行功能測試時剖析檔案(而非在執行
clean
時剖析)。
Gradle 使用兩個介面表示延遲屬性
- Provider
-
表示只能查詢但無法變更的值。
-
具有這些類型的屬性為唯讀。
-
方法 Provider.get() 傳回屬性的目前值。
-
可以使用 Provider.map(Transformer) 從另一個
Provider
建立Provider
。 -
許多其他類型會延伸
Provider
,並可以在需要Provider
的任何地方使用。
-
- Property
-
表示可以查詢和變更的值。
-
具有這些類型的屬性可設定。
-
Property
會延伸Provider
介面。 -
方法 Property.set(T) 會指定屬性的值,覆寫可能存在的任何值。
-
方法 Property.set(Provider) 會指定屬性值的
Provider
,覆寫可能存在的任何值。這允許您在設定值之前將Provider
和Property
實例連結在一起。 -
可以使用工廠方法 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
工作具有 Property<String>
類型的屬性來表示可設定的問候語,以及 Provider<String>
類型的屬性來表示計算的唯讀訊息。訊息 Provider
是使用 map()
方法從問候語 Property
建立的;其值會隨著問候語屬性值的變更而保持最新。
建立 Property 或 Provider 實例
Provider
或其子類型(例如 Property
)不應由建置指令碼或外掛程式實作。Gradle 提供工廠方法來建立這些類型的實例。
在先前的範例中,展示了兩個工廠方法
-
ObjectFactory.property(Class) 建立新的
Property
執行個體。可以從 Project.getObjects() 參照 ObjectFactory 執行個體,或透過建構函式或方法注入ObjectFactory
。 -
Provider.map(Transformer) 從現有的
Provider
或Property
執行個體建立新的Provider
。
請參閱 快速參考 以取得所有可用的類型和工廠。
也可以透過工廠方法 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) 方法,以將 Provider
附加到 Property
以提供屬性的值。在此情況下,Provider
碰巧也是 Property
,但您可以連接任何 Provider
實作,例如使用 Provider.map()
建立的實作
處理檔案
在 處理檔案 中,我們介紹了四種類似 File
的物件的集合類型
唯讀類型 | 可設定類型 |
---|---|
這些類型也都被視為延遲類型。
Gradle 提供了兩個專門的 Property
子類型來處理這些類型的值:RegularFileProperty 和 DirectoryProperty。 ObjectFactory 有方法來建立這些:ObjectFactory.fileProperty() 和 ObjectFactory.directoryProperty()。
DirectoryProperty
也可用於透過 DirectoryProperty.dir(String) 和 DirectoryProperty.file(String) 分別為 Directory
和 RegularFile
建立延遲評估的 Provider
。這些方法建立的提供者其值相對於建立它們的 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() 建立代表專案和建置目錄中位置的提供者。
為了完成迴圈,請注意 DirectoryProperty
或簡單的 Directory
可以轉換成 FileTree
,讓目錄中包含的檔案和目錄可以使用 DirectoryProperty.getAsFileTree() 或 Directory.getAsFileTree() 查詢。從 DirectoryProperty
或 Directory
,您可以使用 DirectoryProperty.files(Object...) 或 Directory.files(Object...) 建立包含目錄中一組檔案的 FileCollection
執行個體。
處理任務輸入和輸出
許多組建有幾個連接在一起的任務,其中一個任務會使用另一個任務的輸出作為輸入。
為了讓這項工作順利進行,我們需要設定每個任務,讓它們知道去哪裡尋找輸入,以及將輸出放置在哪裡。確保產生任務和使用任務設定在相同的位置,並在任務之間附加任務相依性。如果這些值中的任何一個都可以由使用者設定,或由多個外掛程式設定,這可能會很麻煩且脆弱,因為任務屬性需要按正確的順序和位置設定,並且任務相依性會隨著值的變更而保持同步。
Property
API 會追蹤屬性的值和產生該值的任務,讓這項工作變得更輕鬆。
舉例來說,考慮以下包含產生任務和使用任務的外掛程式,它們會互相連接
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
在上面的範例中,任務輸出和輸入會在定義任何位置之前連接。設定器可以在任務執行前隨時呼叫,而變更會自動影響所有相關的輸入和輸出屬性。
此範例中另一個要注意的重要事項是沒有任何明確的任務相依性。使用 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。
您可以使用 ObjectFactory.listProperty(Class) 建立新的ListProperty
,並指定元素類型。 -
對於
Set
值,介面稱為 SetProperty。
您可以使用 ObjectFactory.setProperty(Class) 建立新的SetProperty
,並指定元素類型。
這種類型的屬性允許您使用 HasMultipleValues.set(Iterable) 和 HasMultipleValues.set(Provider) 覆寫整個集合值,或透過各種 add
方法新增新元素
-
HasMultipleValues.add(T):將單一元素新增到集合
-
HasMultipleValues.add(Provider):將延遲計算的元素新增到集合
-
HasMultipleValues.addAll(Provider):將延遲計算的元素集合新增到清單
就像每個 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
值。您可以使用 ObjectFactory.mapProperty(Class, Class) 建立 MapProperty
實例。
與其他屬性類型類似,MapProperty
有 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()
方法來執行此操作。此方法接受值或 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()
當屬性的值來自 Provider
時,會查詢提供者以取得其目前值,而結果會成為屬性的最終值。此最終值會取代提供者,且屬性不再追蹤提供者的值。呼叫此方法也會使屬性實例無法修改,且任何進一步變更屬性值的嘗試都會失敗。Gradle 會在任務開始執行時自動使任務的屬性變為最終值。
finalizeValueOnRead() 方法類似,但屬性的最終值不會計算,直到查詢屬性的值為止。
modifiedFiles.finalizeValueOnRead()
換句話說,此方法會在需要時計算最終值,而 finalizeValue()
則會立即計算最終值。此方法可用於計算值可能很昂貴或尚未設定的情況。您還想要確保屬性的所有使用者在查詢值時看到相同的值。
使用 Provider API
成功使用 Provider API 的準則
Provider 檔案 API 參考
對唯讀值使用這些類型
- Provider<RegularFile>
-
磁碟上的檔案
- Provider<Directory>
-
磁碟上的目錄
- FileCollection
-
非結構化的檔案集合
- FileTree
-
檔案階層
屬性檔案 API 參考
對可變動值使用這些類型
- RegularFileProperty
-
磁碟上的檔案
- DirectoryProperty
-
磁碟上的目錄
- ConfigurableFileCollection
-
非結構化的檔案集合
- ConfigurableFileTree
-
檔案階層
- SourceDirectorySet
-
來源目錄階層
延遲物件 API 參考
對唯讀值使用這些類型
- Provider<T>
-
屬性值為
T
執行個體- 工廠
-
-
ProviderFactory.provider(Callable)。永遠優先使用其他工廠方法而非此方法。
對可變動值使用這些類型
- Property<T>
-
屬性值為
T
執行個體