Android Gradle 插件 (AGP) 是官方的 Android 應(yīng)用構(gòu)建系統(tǒng)。它支持編譯許多不同類型的源代碼吴菠,以及將其鏈接到可在實體 Android 設(shè)備或模擬器上運行的應(yīng)用中橄教。AGP 包含插件擴展點清寇,用于控制 build 輸入并通過可與標準 build 任務(wù)集成的新步驟擴展其功能。
Gradle build 基礎(chǔ)知識
本指南未涵蓋整個 Gradle 構(gòu)建系統(tǒng)护蝶。不過华烟,它涵蓋了與我們的 API 集成所需的最基本的概念。該指南還鏈接到了主 Gradle 文檔持灰,以便您進一步了解相關(guān)信息盔夜。
我們假定您對 Gradle 的工作原理已有基本了解,包括如何配置項目、修改 build 文件喂链、應(yīng)用插件以及運行任務(wù)返十。如需了解關(guān)于 AGP 的 Gradle 基礎(chǔ)知識,我們建議您查看配置 build椭微。如需了解用于自定義 Gradle 插件的通用框架洞坑,請參閱開發(fā)自定義 Gradle 插件。
Gradle 延遲類型術(shù)語表
Gradle 提供多種類型蝇率,它們可表現(xiàn)出“延遲”行為检诗,或者幫助將繁重的計算或 Task
創(chuàng)建推遲到 build 的后續(xù)階段。這些類型是許多 Gradle 和 AGP API 的核心瓢剿。以下列表包含延遲執(zhí)行涉及的主要 Gradle 類型及其主要方法。
<dl style="box-sizing: inherit; margin: 0px; padding: 0px; color: rgb(32, 33, 36); font-family: "Google Sans Text", "Noto Sans", "Noto Sans JP", "Noto Sans KR", "Noto Naskh Arabic", "Noto Sans Thai", "Noto Sans Hebrew", "Noto Sans Bengali", sans-serif; font-size: 16px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(255, 255, 255); text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;">
<dt style="box-sizing: inherit; font: 700 16px/24px var(--devsite-primary-font-family); margin: 16px 0px;">Provider<T>
</dt>
<dd style="box-sizing: inherit; margin: 16px 0px; padding: 0px 0px 0px 40px;">提供類型為 T
的值(其中“T”表示任意類型)悠轩,該值可以在執(zhí)行階段使用 get()
讀取间狂,也可以使用 map()
、flatMap()
和 zip()
方法轉(zhuǎn)換為新的 Provider<S>
(其中“S”表示其他類型)火架。請注意鉴象,切勿在配置階段調(diào)用 get()
。
-
map()
:接受 lambda 并生成類型為S
的Provider
何鸡,即Provider<S>
纺弊。map()
的 lambda 參數(shù)會采用值T
并生成值S
。系統(tǒng)不會立即執(zhí)行 lambda骡男,而是會推遲到在生成的Provider<S>
上調(diào)用get()
時執(zhí)行淆游,從而讓整個鏈條變得延遲。 -
flatMap()
:同樣會接受 lambda 并生成Provider<S>
隔盛,但 lambda 會采用值T
并生成Provider<S>
(而不是直接生成值S
)犹菱。如果在配置時無法確定 S 且您只能獲得Provider<S>
,請使用 flatMap()吮炕。實際上腊脱,如果您使用了map()
并且最終生成的類型為Provider<Provider<S>>
,則可能表示您本該使用flatMap()
龙亲。 -
zip()
:可讓您結(jié)合兩個Provider
實例以生成新的Provider
陕凹,其值是使用將兩個輸入Providers
實例的值結(jié)合的函數(shù)計算得出的。
</dd>
<dt style="box-sizing: inherit; font: 700 16px/24px var(--devsite-primary-font-family); margin: 16px 0px;">Property<T>
</dt>
<dd style="box-sizing: inherit; margin: 16px 0px; padding: 0px 0px 0px 40px;">實現(xiàn) Provider<T>
鳄炉,這樣會提供類型為 T
的值杜耙。與只讀的 Provider<T>
不同,您還可以為 Property<T>
設(shè)置值拂盯。為其設(shè)置值的方法有兩種:
- 在可行的情況下直接設(shè)置類型為
T
的值泥技,而無需推遲計算。 - 將另一個
Provider<T>
設(shè)置為Property<T>
的值的來源。在這種情況下珊豹,值T
只會在系統(tǒng)調(diào)用Property.get()
時具體化簸呈。
</dd>
<dt style="box-sizing: inherit; font: 700 16px/24px var(--devsite-primary-font-family); margin: 16px 0px;">TaskProvider
</dt>
<dd style="box-sizing: inherit; margin: 16px 0px; padding: 0px 0px 0px 40px;">實現(xiàn) Provider<Task>
。如需生成 TaskProvider
店茶,請使用 tasks.register()
蜕便,而不是 tasks.create()
,以確保任務(wù)只在需要時才會延遲實例化贩幻。在 Task
創(chuàng)建之前轿腺,您可以使用 flatMap()
來訪問該 Task
的輸出,如果您想將此輸出用作其他 Task
實例的輸入丛楚,這會很實用族壳。</dd>
</dl>
如要以延遲方式設(shè)置任務(wù)的輸入和輸出,提供程序及其轉(zhuǎn)換方法至關(guān)重要趣些,也就是說仿荆,無需預(yù)先創(chuàng)建所有任務(wù)并解析值。
提供程序還攜帶有任務(wù)依賴項信息坏平。當您通過轉(zhuǎn)換一個 Task
的輸出來創(chuàng)建 Provider
時拢操,該 Task
會成為相應(yīng) Provider
的隱式依賴項,無論 Provider
的值在何時進行解析(例如當另一個 Task
需要它時)舶替,系統(tǒng)都要會創(chuàng)建并運行該 Task令境。
下面的示例展示了如何注冊 GitVersionTask
和 ManifestProducerTask
這兩個任務(wù),同時將 Task
實例的創(chuàng)建推遲到實際需要時顾瞪。ManifestProducerTask
輸入值設(shè)置成了從 GitVersionTask
的輸出獲取的 Provider
舔庶,因此 ManifestProducerTask
隱式依賴于 GitVersionTask
。
// Register a task lazily to get its TaskProvider.
val gitVersionProvider: TaskProvider =
project.tasks.register("gitVersionProvider", GitVersionTask::class.java) {
it.gitVersionOutputFile.set(
File(project.buildDir, "intermediates/gitVersionProvider/output")
)
}
...
/**
* Register another task in the configuration block (also executed lazily,
* only if the task is required).
*/
val manifestProducer =
project.tasks.register(variant.name + "ManifestProducer", ManifestProducerTask::class.java) {
/**
* Connect this task's input (gitInfoFile) to the output of
* gitVersionProvider.
*/
it.gitInfoFile.set(gitVersionProvider.flatMap(GitVersionTask::gitVersionOutputFile))
}
這兩個任務(wù)只會在被明確請求時才會執(zhí)行陈醒。這種請求會發(fā)生在 Gradle 調(diào)用的過程中栖茉,例如,當您運行 ./gradlew debugManifestProducer
時孵延,或者當您將 ManifestProducerTask
的輸出關(guān)聯(lián)到其他某些任務(wù)并且必須使用其值時吕漂。
盡管您將編寫會使用輸入和/或生成輸出的自定義任務(wù),但 AGP 不直接提供對其任務(wù)的公開訪問權(quán)限尘应。這些權(quán)限屬于實現(xiàn)細節(jié)惶凝,每個版本都不一樣。不過犬钢,AGP 會提供 Variant API 及對其任務(wù)輸出的訪問權(quán)限苍鲜,或者提供可供您讀取和轉(zhuǎn)換的 build 工件。如需了解詳情玷犹,請參閱本文檔的 Variant API混滔、工件和任務(wù)部分。
Gradle build 階段
項目構(gòu)建本身是一個復(fù)雜且耗費資源的過程,并且涉及各種功能坯屿,例如任務(wù)配置規(guī)避油湖、更新檢查和配置緩存功能,這些功能有助于最大程度減少花在可重現(xiàn)或非必要計算上的時間领跛。
若要應(yīng)用其中某些優(yōu)化功能乏德,Gradle 腳本和插件在以下每個不同的 Gradle build 階段都必須遵循嚴格的規(guī)則:初始化、配置和執(zhí)行吠昭。在本指南中喊括,我們將重點介紹配置階段和執(zhí)行階段。如需詳細了解所有階段矢棚,請參閱 Gradle build 生命周期指南郑什。
配置階段
在配置階段,系統(tǒng)會評估 build 涉及到的所有項目的 build 腳本蒲肋、應(yīng)用插件并解析 build 依賴項蘑拯。此階段應(yīng)該用來使用 DSL 對象配置 build,以及以延遲方式注冊任務(wù)及其輸入肉津。
由于配置階段總是在運行,因此無論系統(tǒng)請求運行哪個任務(wù)舱沧,都務(wù)必讓該階段保持精簡妹沙,并對所有計算進行限制,防止其依賴于輸入而不是 build 腳本本身熟吏。也就是說距糖,您不應(yīng)執(zhí)行外部程序或從網(wǎng)絡(luò)中讀取內(nèi)容,也不應(yīng)執(zhí)行可以作為合適的 Task
實例推遲到執(zhí)行階段的長時間計算牵寺。
注意:使用即將推出的配置緩存功能悍引,此階段的結(jié)果將被緩存,以供后續(xù)運行帽氓,但前提是要特別注意讓構(gòu)建系統(tǒng)知道所有動態(tài)輸入趣斤,例如文件讀取或?qū)Νh(huán)境變量的訪問。