Android Gradle 外掛程式 (AGP) 是 Android 應用程式的官方建構系統。這套系統支援編譯多種不同類型的來源,並連結成為應用程式,供您在實體 Android 裝置或模擬器上執行。
AGP 包含外掛程式的擴充點,用於控制建構輸入,也能透過與標準建構任務整合的新步驟以擴充其功能。舊版 AGP 沒有能與內部實作清楚區分的官方 API。從 7.0 版本開始,AGP 提供一組官方穩定 API,為您提供可靠服務。
AGP API 生命週期
AGP 遵循 Gradle 功能生命週期,以標示其 API 的狀態:
- Internal:不開放公開使用
- Incubating:開放公開使用,但非最終版本,也就是說,這個版本可能無法提供最終版本的回溯相容性
- Public:開放公開使用且功能穩定
- 「Deprecated」(已淘汰):系統不再支援該 API,已由新的 API 取代
廢止政策
隨著舊版 API 的淘汰,並且替換成全新且穩定的 API 和全新特定領域語言 (DSL),AGP 實現持續的發展。此更新將涵蓋多個 AGP 版本,詳情請參閱 AGP API/DSL 遷移時限。
當 AGP API 淘汰後,無論是本次遷移或其他原因,您仍可以在目前的主要版本中繼續使用,但會收到警告。在後續的主要版本中,已淘汰的 API 將從 AGP 中完全移除。例如,如果 API 已在 AGP 7.0 中淘汰,API 仍能在該版本中使用,但會產生警告。而 AGP 8.0 將不再提供此 API。
若要查看常見建構自訂項目的新 API 範例,請參閱 Android Gradle 外掛程式方案。該方案提供常見建構自訂範例。您也可以參閱參考說明文件,進一步瞭解全新 API。
Gradle 建構的基本概念
本指南並未涵蓋全部 Gradle 建構系統。不過,其中涵蓋了與 API 整合所需的最基本概念,並提供主要 Gradle 文件的連結,方便您閱讀。
我們會假設 Gradle 的運作方式,包括如何設定專案、編輯建構檔案、套用外掛程式及執行工作。如要瞭解 Gradle 關於 AGP 的基本知識,建議您參閱設定您的建構項目。如要瞭解自訂 Gradle 外掛程式的一般架構,請參閱「開發自訂 Gradle 外掛程式」一文。
Gradle 延遲類型詞彙表
Gradle 提供許多類型,包括「延遲」運作方式,或為後續建構階段延後重覆運算或創建 Task
。這些類型是許多 Gradle 和 AGP API 的核心。下方列出延遲執行的主要 Gradle 類型及其金鑰方法。
Provider<T>
- 提供
T
類型的值 (其中「T」代表任何類型),可在執行階段使用get()
讀取,或使用map()
、flatMap()
和zip()
方法轉換為新的Provider<S>
(其中「S」表示其他類型)。請注意,在設定階段不可呼叫get()
。map()
:接受 lambda,產生S
、Provider<S>
類型的Provider
。map()
的 lambda 引數採用T
值並產生S
值。lambda 不會立即執行;而是被延遲到對結果Provider<S>
呼叫get()
的時刻,讓整個鏈接延遲完成。flatMap()
:同時接受 lambda 並產生Provider<S>
,但 lambda 採用T
並產生Provider<S>
(而非直接產生值S
)。如果在設定時間無法判斷 S,則使用 flatMap(),且您僅能取得Provider<S>
。簡單來說,如果您使用map()
且最終取得Provider<Provider<S>>
結果類型,這可能表示您應改用flatMap()
。zip()
:您可合併兩個Provider
例項,產生一個新的Provider
。其中的值是使用函式,合併這兩個輸入的Providers
例項所提供的值計算得出。
Property<T>
- 導入
Provider<T>
,這樣會提供T
類型的值。與Provider<T>
(唯讀) 不同,您也可以設定Property<T>
的值。您可以透過以下兩種方式完成:- 如有,請直接設定
T
類型的值,這樣就不必使用延遲計算。 - 將另一個
Provider<T>
設為Property<T>
值的來源。在此情況下,只有在呼叫Property.get()
時,值T
才能實現。
- 如有,請直接設定
TaskProvider
- 實作
Provider<Task>
。如要產生TaskProvider
,請使用tasks.register()
而非tasks.create()
,確保工作只在需要時才延遲執行個體化。您可以在創建Task
之前使用flatMap()
獲取Task
的輸出內容,很適合用來將輸出作為其他Task
執行個體的輸入。
供應商及其轉換方法以延遲方式設定工作的輸入和輸出內容,也就是不需要預先建立所有工作以及解析值。
提供者也會提供工作依附元件資訊。建立 Provider
方法是轉換 Task
輸出,Task
變成 Provider
隱式依附元件,並且將在解析 Provider
的值時創建並執行,例如當另一個 Task
需要時。
下列是註冊 GitVersionTask
和 ManifestProducerTask
兩項工作的範例,在實際需要前,會延遲建立 Task
執行個體。ManifestProducerTask
輸入值會設為從 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))
}
這兩項工作只會在明確要求時執行。Gradle 叫用時會發生此情況,例如您執行 ./gradlew
debugManifestProducer
,或 ManifestProducerTask
的輸出內容已連結至其他工作,且必須有值時。
雖然您可以編寫會耗用輸入和/或輸出的自訂工作,但 AGP 不會直接提供自己工作的公開存取權。這些實作項目的詳細資料可能因版本不同而改變。相反,AGP 提供 Variant API 並提供工作輸出的存取權或建構成果,方便您讀取及轉換。詳情請參閱本文件中的 Variant API、成果和工作。
Gradle 建構階段
建構專案本身相當複雜,是需要資源的過程,且擁有許多功能,例如避免工作設定、最新的檢查項目,以及設定快取功能,協助您盡量縮短可重現或不必要運算的耗時。
Gradle 指令碼和外掛程式必須在各個不同的 Gradle 建構階段 (即初始化、設定和執行) 中遵守嚴格的規則,才能套用其中幾項最佳化。在本指南中,我們將著重說明設定和執行階段。如要進一步瞭解所有階段,請參閱 Gradle 建構生命週期指南。
設定階段
在設定階段,系統會評估構成該版本的所有專案的建構指令碼,並套用外掛程式和解決依附元件。這個階段應使用 DSL 物件來設定建構作業,並延遲註冊工作及其輸入內容。
無論要求執行何種工作,設定階段一律都會執行,因此請盡量保持精簡,並根據建構指令碼本身以外的輸入內容限制任何運算作業。換言之,您不應執行外部程式或從網路讀取資料,也不得執行延後至執行階段的長時間運算,做為適當的 Task
執行個體。
執行階段
在執行階段,系統會執行要求的工作及其相依工作。具體來說,系統會執行標示為 @TaskAction
的 Task
類別方法。在工作執行期間,您可以呼叫 Provider<T>.get()
來讀取輸入內容 (例如檔案) 並讀取延遲供應商。透過此方式解決延遲供應商之後,系統會啟動一系列遵循供應商工作依附元件資訊的 map()
或 flatMap()
呼叫。延遲執行工作以實現必要值。
Variant API、成果和工作
Variant API 是 Android Gradle 外掛程式中的擴充功能,可讓您處理各種選項 (通常使用建構設定檔中的 DSL 來設定),這會影響 Android 版本。Variant API 也能讓您存取建構創建的中繼與最終構件,例如類別檔案、合併資訊清單或 APK/AAB 檔案。
Android 建構流程和擴充點
與 AGP 互動時,請使用特製的擴充點,而非註冊一般 Gradle 生命週期回呼 (例如 afterEvaluate()
),或設定明確 Task
依附元件。AGP 建立的工作會視為實作詳情,不會以公開 API 的形式顯示。您必須避免嘗試取得 Task
物件的執行個體,或是猜測 Task
名稱,並直接在這些 Task
物件中新增回呼或依附元件。
AGP 會完成下列步驟來建構並執行其 Task
執行個體,如此就會產生建構構件。建立 Variant
物件的主要步驟隨後是回呼,讓您可以在建構作業中變更創建的特定物件。請特別注意,所有回呼都會在設定階段 (如本頁所述) 進行且必須快速執行,同時將任何複雜的工作改為延遲到執行階段,由正確的 Task
例項執行。
- DSL 剖析:在評估建構指令碼時,系統會創建和設定
android
區塊的 Android DSL 物件的各種屬性。以下各節將介紹下列 Variant API 回呼。 finalizeDsl()
:回呼可讓您在變更元件 (變化版本) 鎖定之前變更 DSL 物件。系統會根據 DSL 物件包含的資料建立VariantBuilder
物件。DSL 鎖定:DSL 現已鎖定,無法再進行變更。
beforeVariants()
:此回呼可能會影響透過VariantBuilder
建立的元件及其屬性。但仍可以修改建構流程和產生的構件。建立變化版本:即將建立的元件和成果清單現已確定,且無法變更。
onVariants()
: 在這個回呼中,您可以存取已建立的Variant
,您可以為它們包含的Property
值設定值或供應商,以延遲運算。變化版本鎖定:變化版本物件現已鎖定,無法再變更。
已建立工作:
Variant
物件及其Property
值,用來建立執行建構所需的Task
執行個體。
AGP 推出了 AndroidComponentsExtension
,可讓您註冊 finalizeDsl()
、beforeVariants()
和 onVariants()
的回呼。可使用 androidComponents
區塊的建構指令碼取得擴充功能:
// This is used only for configuring the Android build through DSL.
android { ... }
// The androidComponents block is separate from the DSL.
androidComponents {
finalizeDsl { extension ->
...
}
}
不過,建議您僅使用 Android 區塊的 DSL 進行宣告設定,藉此建立版本指令碼,並將任何自訂模擬邏輯移至 buildSrc
或外部外掛程式。此外,您也可以查看 Gradle 方案 GitHub 存放區中的 buildSrc
範例,瞭解如何在專案中建立外掛程式。以下是透過外掛程式的程式碼註冊回呼的範例:
abstract class ExamplePlugin: Plugin<Project> {
override fun apply(project: Project) {
val androidComponents = project.extensions.getByType(AndroidComponentsExtension::class.java)
androidComponents.finalizeDsl { extension ->
...
}
}
}
以下將進一步說明可用的回呼,以及外掛程式可支援的回呼類型:
finalizeDsl(callback: (DslExtensionT) -> Unit)
在此回呼中,透過剖析構建檔案中 android
區塊的資訊,而建立的 DSL 物件,您可以進行存取及修改。這些 DSL 物件會在後續版本的建構階段中初始化及設定變化版本。例如,您可以程式輔助的方式建立新的設定或覆寫屬性,但請注意,所有值都必須在設定期間完成解析,因此不得仰賴任何外部輸入內容。此回呼完成執行之後,DSL 物件就不再實用,不應再包含參照物件或進行修改。
abstract class ExamplePlugin: Plugin<Project> {
override fun apply(project: Project) {
val androidComponents = project.extensions.getByType(AndroidComponentsExtension::class.java)
androidComponents.finalizeDsl { extension ->
extension.buildTypes.create("extra").let {
it.isJniDebuggable = true
}
}
}
}
beforeVariants()
在建構作業的此階段中,您可以存取 VariantBuilder
物件,藉此決定要建立的變化版本和屬性。例如,您可以透過程式停用某些變化版本、其測試,或僅變更所選變化版本的屬性值 (例如 minSdk
)。與 finalizeDsl()
類似,您提供的所有值都必須在設定時間解決,且不得依賴外部輸入。執行完 beforeVariants()
回呼後,您就無法修改 VariantBuilder
物件。
androidComponents {
beforeVariants { variantBuilder ->
variantBuilder.minSdk = 23
}
}
beforeVariants()
回呼可選擇接受一個 VariantSelector
,您可以透過 androidComponentsExtension
上的 selector()
方法獲取。您可以使用此名稱,根據名稱、版本類型或變種版本篩選參與回呼叫用的元件。
androidComponents {
beforeVariants(selector().withName("adfree")) { variantBuilder ->
variantBuilder.minSdk = 23
}
}
onVariants()
呼叫 onVariants()
時,AGP 建立的所有構件均已決定,因此您無法再停用。但是,您可以在 Variant
物件中設定 Property
屬性,藉此修改工作使用的部分值。由於僅在執行 AGP 工作時才會解析 Property
值,因此您可以放心地將其從自訂工作轉至供應商,以便執行任何必要的運算,包括從外部輸入讀取檔案或網路等。
// onVariants also supports VariantSelectors:
onVariants(selector().withBuildType("release")) { variant ->
// Gather the output when we are in single mode (no multi-apk).
val mainOutput = variant.outputs.single { it.outputType == OutputType.SINGLE }
// Create version code generating task
val versionCodeTask = project.tasks.register("computeVersionCodeFor${variant.name}", VersionCodeTask::class.java) {
it.outputFile.set(project.layout.buildDirectory.file("${variant.name}/versionCode.txt"))
}
/**
* Wire version code from the task output.
* map() will create a lazy provider that:
* 1. Runs just before the consumer(s), ensuring that the producer
* (VersionCodeTask) has run and therefore the file is created.
* 2. Contains task dependency information so that the consumer(s) run after
* the producer.
*/
mainOutput.versionCode.set(versionCodeTask.map { it.outputFile.get().asFile.readText().toInt() })
}
將產生的來源提供給建構作業
外掛程式可提供幾種類型的產生來源,例如:
java
目錄中的應用程式程式碼res
目錄中的 Android 資源resources
目錄中的 Java 資源assets
目錄中的 Android 資產
如需可供新增的完整來源清單,請參閱 Sources API 相關說明。
此程式碼片段示範如何使用 addStaticSourceDirectory()
函式,將名為 ${variant.name}
的自訂來源資料夾新增至 Java 來源集。Android 工具鏈會接著處理這個資料夾。
onVariants { variant ->
variant.sources.java?.let { java ->
java.addStaticSourceDirectory("custom/src/kotlin/${variant.name}")
}
}
詳情請參閱「addJavaSource 方案」。
這個程式碼片段示範如何將含有自訂工作所產生 Android 資源的目錄,新增至 res
來源集。這套程序與其他來源類型的程序很類似。
onVariants(selector().withBuildType("release")) { variant ->
// Step 1. Register the task.
val resCreationTask =
project.tasks.register<ResCreatorTask>("create${variant.name}Res")
// Step 2. Register the task output to the variant-generated source directory.
variant.sources.res?.addGeneratedSourceDirectory(
resCreationTask,
ResCreatorTask::outputDirectory)
}
...
// Step 3. Define the task.
abstract class ResCreatorTask: DefaultTask() {
@get:OutputFiles
abstract val outputDirectory: DirectoryProperty
@TaskAction
fun taskAction() {
// Step 4. Generate your resources.
...
}
}
詳情請參閱「addCustomAsset 方案」。
存取及修改構件
除了讓您修改 Variant
物件的簡易屬性以外,AGP 還提供擴充機制,讓您讀取或轉換在建構期間產生的中繼與最終構件。例如,您可以讀取自訂 Task
中的最終合併 AndroidManifest.xml
檔案內容進行分析,也可將自訂 Task
產生的資訊清單檔案完全換成其中的內容。
您可以在 Artifact
類別的參考說明文件中找到目前支援的構件清單。每個成果類型都有以下特定屬性,值得您瞭解:
基數
Artifact
的基數代表其 FileSystemLocation
執行個體數量,或文檔的數量或成果類型的目錄。您可以檢查其父項類別,取得成果的基數資訊:含有單一 FileSystemLocation
的成果為 Artifact.Single
的子類別;具有多個 FileSystemLocation
執行個體的成果會成為 Artifact.Multiple
的子類別。
FileSystemLocation
種類型
您可以通過查看其參數化的 FileSystemLocation
類型來檢查 Artifact
是否代表文檔或目錄,該類型可以是 RegularFile
或 Directory
。
支援的操作
每個 Artifact
類別皆可實作下列任何介面來指出支援的操作:
Transformable
:允許Artifact
用作Task
的輸入,對其執行任意轉換,並輸出新版Artifact
。Appendable
:僅適用於屬於Artifact.Multiple
子類別的構件。這表示Artifact
可附加至其他元素,也就是說,自訂Task
可以建立此Artifact
類型的新例項並新增至現有清單。Replaceable
:僅適用於屬於Artifact.Single
子類別的構件。替代的Artifact
可以換成全新的執行個體,做為Task
的輸出內容。
除了三個成果修改操作之外,每個成果還支援 get()
(或 getAll()
) 操作,會傳回含有最終成果版本的 Provider
(完成所有操作後)。
多個外掛程式可以透過 onVariants()
回呼,將更多構件操作新增至管道,AGP 可確保這些鏈結作業皆正確無誤,確保所有工作能在適當時間執行,正確產生成果並更新。也就是說,當操作透過附加、取代或轉換的方式變更任何輸出內容時,下一個操作會將這些構件的更新版本視為輸入內容,依此類推。
註冊操作的進入點為 Artifacts
類別。下列程式碼片段說明如何透過 onVariants()
回呼的 Variant
物件上存取 Artifacts
的執行個體。
接著,您可以傳遞自訂 TaskProvider
來取得 TaskBasedOperation
物件 (1),並使用其中一個 wiredWith*
方法 (2) 來連接其輸入和輸出。
您需要選擇的確切方法,取決於您想轉換的 Artifact
實作的基數和 FileSystemLocation
類型。
最後,您必須將 Artifact
類型傳遞至一個方法,該方法表示您在回傳的 *OperationRequest
物件上所選的操作,例如 toAppendTo()
、
toTransform()
或 toCreate()
(3)。
androidComponents.onVariants { variant ->
val manifestUpdater = // Custom task that will be used for the transform.
project.tasks.register(variant.name + "ManifestUpdater", ManifestTransformerTask::class.java) {
it.gitInfoFile.set(gitVersionProvider.flatMap(GitVersionTask::gitVersionOutputFile))
}
// (1) Register the TaskProvider w.
val variant.artifacts.use(manifestUpdater)
// (2) Connect the input and output files.
.wiredWithFiles(
ManifestTransformerTask::mergedManifest,
ManifestTransformerTask::updatedManifest)
// (3) Indicate the artifact and operation type.
.toTransform(SingleArtifact.MERGED_MANIFEST)
}
在這個範例中,MERGED_MANIFEST
是 SingleArtifact
,且是 RegularFile
。因此,我們需要使用 wiredWithFiles
方法,接受輸入的單一 RegularFileProperty
參照,以及接受輸出的單一 RegularFileProperty
。TaskBasedOperation
類別中的其他 wiredWith*
方法可搭配其他 Artifact
基數和 FileSystemLocation
類型組合使用。
如要進一步瞭解如何擴充 AGP,建議您從 Gradle 建構系統手冊參閱下列章節: