Das Android-Gradle-Plug-in (AGP) ist das offizielle Build-System für Android-Anwendungen. Es unterstützt das Kompilieren vieler verschiedener Arten von Quellen und das Verknüpfen dieser Quellen zu einer Anwendung, die Sie auf einem physischen Android-Gerät oder einem Emulator ausführen können.
Das AGP enthält Erweiterungspunkte für Plug-ins, mit denen Build-Eingaben gesteuert und die Funktionen durch neue Schritte erweitert werden können, die sich in Standard-Build-Aufgaben einbinden lassen. In früheren Versionen des AGP waren die offiziellen APIs nicht klar von den internen Implementierungen getrennt. Ab Version 7.0 bietet das AGP eine Reihe offizieller, stabiler APIs, auf die Sie sich verlassen können.
Lebenszyklus der AGP API
Das AGP folgt dem Gradle-Feature-Lebenszyklus, um den Status seiner APIs zu kennzeichnen:
- Intern: Nicht für die öffentliche Verwendung bestimmt
- In der Inkubationsphase: Für die öffentliche Nutzung verfügbar, aber nicht endgültig. Das bedeutet, dass sie in der endgültigen Version möglicherweise nicht abwärtskompatibel sind.
- Öffentlich: Für die öffentliche Nutzung verfügbar und stabil
- Verworfen: Wird nicht mehr unterstützt und durch neue APIs ersetzt
Richtlinie zur Einstellung von Produkten und Diensten
Das AGP wird weiterentwickelt, indem alte APIs eingestellt und durch neue, stabile APIs und eine neue domänenspezifische Sprache (DSL) ersetzt werden. Diese Entwicklung wird sich über mehrere AGP-Releases erstrecken. Weitere Informationen finden Sie im Zeitplan für die AGP API/DSL-Migration.
Wenn AGP APIs für diese Migration oder anderweitig eingestellt werden, sind sie in der aktuellen Hauptversion weiterhin verfügbar, generieren aber Warnungen. Verworfene APIs werden in der nächsten Hauptversion vollständig aus dem AGP entfernt. Wenn eine API beispielsweise in AGP 7.0 verworfen wird, ist sie in dieser Version verfügbar und generiert Warnungen. Diese API ist in AGP 8.0 nicht mehr verfügbar.
Beispiele für neue APIs, die in gängigen Build-Anpassungen verwendet werden, finden Sie in den Android Gradle-Plug-in-Rezepten. Sie enthalten Beispiele für gängige Build-Anpassungen. Weitere Informationen zu den neuen APIs finden Sie in unserer Referenzdokumentation.
Mit dem AGP API-Artefakt kompilieren
Wenn Sie ein benutzerdefiniertes Gradle-Plug-in oder eine Build-Logik schreiben möchten (z. B. im Verzeichnis buildSrc, in einem Konventions-Plug-in-Modul oder in einem eigenständigen Plug-in-Projekt), die das AGP erweitert, kompilieren Sie gegen das Artefakt gradle-api des AGP anstatt gegen das vollständige Plug-in-Implementierungsartefakt.
Warum sollte ich mit dem Artefakt gradle-api kompilieren?
Das AGP veröffentlicht zwei primäre Artefakte:
com.android.tools.build:gradle-api: Enthält offizielle AGP-APIs, einschließlich stabiler APIs (wieAndroidComponentsExtension,Variantund öffentliche DSL-Schnittstellen), APIs in der Entwicklungsphase (gekennzeichnet mit@Incubating) und veralteter APIs (gekennzeichnet mit@Deprecated).com.android.tools.build:gradle: Enthält die vollständige AGP-Laufzeitimplementierung, einschließlich interner Klassen, Build-Aufgaben und Compiler-Integrationen.
Das Kompilieren mit dem gradle-api-Artefakt bietet mehrere wichtige Vorteile:
- Interne APIs vermeiden: So wird sichergestellt, dass Ihr Plug-in-Code nur auf offizielle APIs zugreift. Dadurch werden versehentliche Abhängigkeiten von internen Implementierungsklassen (
com.android.build.gradle.internal.*) verhindert, die sich ohne Vorwarnung in verschiedenen AGP-Versionen ändern oder entfernt werden können. - Schnellere Builds und geringerer Speicherbedarf: Das Artefakt
gradle-apihat einen viel kleineren Abhängigkeitsbaum als das vollständige Artefaktgradle. Dadurch werden die Downloadgrößen reduziert, die Verschmutzung des Klassenpfads vermieden und die Kompilierung der Build-Logik beschleunigt. - Laufzeitisolierung: Benutzerdefinierte Plug-ins benötigen API-Definitionen nur während der Kompilierung. Zur Laufzeit wird die vollständige AGP-Implementierung vom Android-Build bereitgestellt, wenn das Android-App- oder Bibliotheks-Plug-in angewendet wird.
Abhängigkeit hinzufügen
Fügen Sie das Artefakt gradle-api als Abhängigkeit in der Build-Datei Ihres Plug-in-Projekts oder im Verzeichnis buildSrc hinzu. Das Artefakt gradle-api wird im Maven-Repository von Google veröffentlicht. Achten Sie darauf, dass Ihr Build das Repository google() enthält, um das Artefakt aufzulösen. Konfigurieren Sie sie beispielsweise im Block dependencyResolutionManagement.repositories der Datei settings.gradle oder settings.gradle.kts oder im Block repositories der Build-Datei Ihres Plug-ins oder der Build-Datei des Verzeichnisses buildSrc.
Mit einem Versionskatalog
Wenn Ihr Projekt einen Versionskatalog verwendet, definieren Sie die Abhängigkeit gradle-api in der Datei gradle/libs.versions.toml:
[versions]
androidGradlePlugin = "9.4.0"
[libraries]
android-gradle-plugin-api = { group = "com.android.tools.build", name = "gradle-api", version.ref = "androidGradlePlugin" }
Der Katalogalias android-gradle-plugin-api generiert den typsicheren Accessor libs.android.gradle.plugin.api.
Fügen Sie dann libs.android.gradle.plugin.api zum dependencies-Block der Build-Datei Ihres Plug-ins hinzu:
Kotlin
dependencies { compileOnly(libs.android.gradle.plugin.api) }
Groovy
dependencies { compileOnly libs.android.gradle.plugin.api }
Ohne Versionskatalog
Wenn in Ihrem Projekt kein Versionskatalog verwendet wird, deklarieren Sie die Abhängigkeit direkt in der Build-Datei Ihres Plug-ins:
Kotlin
dependencies { compileOnly("com.android.tools.build:gradle-api:9.4.0") }
Groovy
dependencies { compileOnly 'com.android.tools.build:gradle-api:9.4.0' }
Verwenden Sie die Konfigurationsoption compileOnly als Standard für eigenständige Plug-ins, die veröffentlicht werden sollen. Das nutzende Projekt wendet das AGP an und stellt seine Laufzeitklassen im Buildscript-Klassenpfad bereit. Durch die compileOnly-Konfiguration wird verhindert, dass Ihr veröffentlichtes Plug-in eine Laufzeitabhängigkeit von einer bestimmten AGP-Version hat. Außerdem werden Versionskonflikte für Nutzer vermieden. Wenn Sie interne Build-Logik entwickeln, z. B. Plug-ins im Verzeichnis buildSrc oder Konventions-Plug-ins in zusammengesetzten Builds, verwenden Sie stattdessen die Konfigurationsoption implementation. Diese Plug-ins werden innerhalb des Builds ausgeführt und erfordern das AGP im Laufzeitklassenpfad.
Grundlagen des Gradle-Builds
In diesem Leitfaden wird nicht das gesamte Gradle-Build-System behandelt. Sie deckt jedoch die mindestens erforderlichen Konzepte ab, die Sie für die Einbindung unserer APIs benötigen, und enthält Links zur Hauptdokumentation von Gradle, in der Sie weitere Informationen finden.
Wir setzen grundlegende Kenntnisse über die Funktionsweise von Gradle voraus, einschließlich der Konfiguration von Projekten, der Bearbeitung von Build-Dateien, der Anwendung von Plug-ins und der Ausführung von Aufgaben. Informationen zu den Grundlagen von Gradle im Zusammenhang mit dem AGP finden Sie unter Build konfigurieren. Informationen zum allgemeinen Framework für das Anpassen von Gradle-Plug-ins finden Sie unter Developing Custom Gradle Plugins.
Glossar zu Lazy-Typen in Gradle
Gradle bietet eine Reihe von Typen, die sich „lazy“ verhalten oder dazu beitragen, rechenintensive Berechnungen oder die Erstellung von Task auf spätere Phasen des Builds zu verschieben. Diese Typen sind das Herzstück vieler Gradle- und AGP-APIs. Die folgende Liste enthält die wichtigsten Gradle-Typen, die an der verzögerten Ausführung beteiligt sind, und ihre wichtigsten Methoden.
Provider<T>- Stellt einen Wert vom Typ
Tbereit (wobei „T“ für einen beliebigen Typ steht), der während der Ausführungsphase mitget()gelesen oder mit den Methodenmap(),flatMap()undzip()in einen neuenProvider<S>(wobei „S“ für einen anderen Typ steht) umgewandelt werden kann. Beachten Sie, dassget()während der Konfigurationsphase nie aufgerufen werden sollte.map(): Akzeptiert ein Lambda und erzeugt einProvidervom TypS,Provider<S>. Das Lambda-Argument fürmap()nimmt den WertTan und erzeugt den WertS. Die Lambda-Funktion wird nicht sofort ausgeführt. Stattdessen wird die Ausführung auf den Moment verschoben, in demget()für das resultierendeProvider<S>aufgerufen wird. Dadurch wird die gesamte Kette verzögert.flatMap(): Akzeptiert auch einen Lambda-Ausdruck und erzeugtProvider<S>, aber der Lambda-Ausdruck nimmt einen WertTund erzeugtProvider<S>(anstatt den WertSdirekt zu erzeugen). Verwenden Sie „flatMap()“, wenn S zum Zeitpunkt der Konfiguration nicht bestimmt werden kann und Sie nurProvider<S>erhalten können. Wenn Siemap()verwendet haben und einen ErgebnistypProvider<Provider<S>>erhalten, bedeutet das wahrscheinlich, dass Sie stattdessenflatMap()hätten verwenden sollen.zip(): Ermöglicht das Kombinieren von zweiProvider-Instanzen, um eine neueProvider-Instanz zu erzeugen. Der Wert wird mit einer Funktion berechnet, die die Werte aus den beidenProviders-Eingabeinstanzen kombiniert.
Property<T>- Implementiert
Provider<T>, sodass auch ein Wert vom TypTbereitgestellt wird. Im Gegensatz zuProvider<T>, das schreibgeschützt ist, können Sie auch einen Wert fürProperty<T>festlegen. Dafür gibt es zwei Möglichkeiten:- Legen Sie einen Wert vom Typ
Tdirekt fest, wenn er verfügbar ist, ohne dass verzögerte Berechnungen erforderlich sind. - Legen Sie ein anderes
Provider<T>als Quelle für den Wert desProperty<T>fest. In diesem Fall wird der WertTnur materialisiert, wennProperty.get()aufgerufen wird.
- Legen Sie einen Wert vom Typ
TaskProvider- Implementiert
Provider<Task>. Verwenden Sie zum Generieren einesTaskProvidertasks.register()und nichttasks.create(), damit Aufgaben nur dann instanziiert werden, wenn sie benötigt werden. MitflatMap()können Sie auf die Ausgaben einerTaskzugreifen, bevor dieTaskerstellt wird. Das kann nützlich sein, wenn Sie die Ausgaben als Eingaben für andereTask-Instanzen verwenden möchten.
Anbieter und ihre Transformationsmethoden sind wichtig, um Eingaben und Ausgaben von Aufgaben verzögert einzurichten, d. h. ohne alle Aufgaben im Voraus erstellen und die Werte auflösen zu müssen.
Anbieter enthalten auch Informationen zu Aufgabenabhängigkeiten. Wenn Sie einen Provider durch Transformieren einer Task-Ausgabe erstellen, wird dieser Task zu einer impliziten Abhängigkeit des Provider. Er wird immer dann erstellt und ausgeführt, wenn der Wert des Provider aufgelöst wird, z. B. wenn ein anderer Task ihn benötigt.
Hier ist ein Beispiel für die Registrierung von zwei Tasks, GitVersionTask und ManifestProducerTask, wobei die Erstellung der Task-Instanzen verschoben wird, bis sie tatsächlich benötigt werden. Der Eingabewert ManifestProducerTask wird auf einen Provider festgelegt, der aus der Ausgabe von GitVersionTask abgerufen wird. Daher hängt ManifestProducerTask implizit von GitVersionTask ab.
// 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))
}
Diese beiden Aufgaben werden nur ausgeführt, wenn sie explizit angefordert werden. Dies kann im Rahmen eines Gradle-Aufrufs geschehen, z. B. wenn Sie ./gradlew
debugManifestProducer ausführen oder wenn die Ausgabe von ManifestProducerTask mit einer anderen Aufgabe verbunden ist und ihr Wert erforderlich wird.
Sie können zwar benutzerdefinierte Aufgaben schreiben, die Eingaben verarbeiten und/oder Ausgaben erzeugen, aber das AGP bietet keinen direkten öffentlichen Zugriff auf seine eigenen Aufgaben. Sie sind ein Implementierungsdetail, das sich von Version zu Version ändern kann. Stattdessen bietet das AGP die Variant API und Zugriff auf die Ausgabe seiner Aufgaben oder Build-Artefakte, die Sie lesen und transformieren können. Weitere Informationen finden Sie in diesem Dokument unter Variant API, Artefakte und Tasks.
Gradle-Build-Phasen
Das Erstellen eines Projekts ist von Natur aus ein komplizierter und ressourcenintensiver Prozess. Es gibt verschiedene Funktionen wie die Vermeidung von Aufgabenkonfigurationen, Aktualitätsprüfungen und das Konfigurations-Caching, die dazu beitragen, den Zeitaufwand für reproduzierbare oder unnötige Berechnungen zu minimieren.
Damit einige dieser Optimierungen angewendet werden können, müssen Gradle-Skripts und ‑Plug-ins in jeder der verschiedenen Gradle-Build-Phasen (Initialisierung, Konfiguration und Ausführung) strenge Regeln einhalten. In diesem Leitfaden konzentrieren wir uns auf die Konfigurations- und Ausführungsphase. Weitere Informationen zu allen Phasen finden Sie im Leitfaden zum Gradle-Build-Lebenszyklus.
Konfigurationsphase
In der Konfigurationsphase werden die Build-Scripts für alle Projekte, die Teil des Builds sind, ausgewertet, die Plug-ins angewendet und Build-Abhängigkeiten aufgelöst. In dieser Phase sollte der Build mithilfe von DSL-Objekten konfiguriert werden. Außerdem sollten Aufgaben und ihre Eingaben verzögert registriert werden.
Da die Konfigurationsphase immer ausgeführt wird, unabhängig davon, welche Aufgabe ausgeführt werden soll, ist es besonders wichtig, sie schlank zu halten und Berechnungen nicht von anderen Eingaben als den Build-Skripts selbst abhängig zu machen.
Das bedeutet, dass Sie keine externen Programme ausführen, nicht aus dem Netzwerk lesen und keine langen Berechnungen durchführen sollten, die als Task-Instanzen auf die Ausführungsphase verschoben werden können.
Ausführungsphase
In der Ausführungsphase werden die angeforderten Aufgaben und die davon abhängigen Aufgaben ausgeführt. Insbesondere werden die mit @TaskAction gekennzeichneten Task-Klassenmethoden ausgeführt. Während der Taskausführung können Sie aus Eingaben (z. B. Dateien) lesen und Lazy Provider durch Aufrufen von Provider<T>.get() auflösen. Wenn Sie Lazy-Provider auf diese Weise auflösen, wird eine Abfolge von map()- oder flatMap()-Aufrufen gestartet, die den im Provider enthaltenen Informationen zur Aufgabenabhängigkeit folgen. Aufgaben werden verzögert ausgeführt, um die erforderlichen Werte zu materialisieren.
Variant API, Artefakte und Tasks
Die Variant API ist ein Erweiterungsmechanismus im Android Gradle-Plug-in, mit dem Sie die verschiedenen Optionen bearbeiten können, die normalerweise mit der DSL in Build-Konfigurationsdateien festgelegt werden und den Android-Build beeinflussen. Über die Variant API können Sie auch auf Zwischen- und Endartefakte zugreifen, die vom Build erstellt werden, z. B. Klassendateien, das zusammengeführte Manifest oder APK-/AAB-Dateien.
Android-Build-Ablauf und Erweiterungspunkte
Verwenden Sie bei der Interaktion mit dem AGP speziell dafür vorgesehene Erweiterungspunkte, anstatt die typischen Gradle-Lebenszyklus-Callbacks (z. B. afterEvaluate()) zu registrieren oder explizite Task-Abhängigkeiten einzurichten. Von AGP erstellte Aufgaben gelten als Implementierungsdetails und werden nicht als öffentliche API bereitgestellt. Sie dürfen nicht versuchen, Instanzen der Task-Objekte abzurufen oder die Task-Namen zu erraten und diesen Task-Objekten direkt Callbacks oder Abhängigkeiten hinzuzufügen.
Das AGP führt die folgenden Schritte aus, um seine Task-Instanzen zu erstellen und auszuführen, die wiederum die Build-Artefakte erzeugen. Auf die Hauptschritte bei der Erstellung von Variant-Objekten folgen Callbacks, mit denen Sie Änderungen an bestimmten Objekten vornehmen können, die im Rahmen eines Builds erstellt wurden. Wichtig: Alle Callbacks erfolgen während der Konfigurationsphase (auf dieser Seite beschrieben) und müssen schnell ausgeführt werden. Komplizierte Aufgaben werden stattdessen während der Ausführungsphase auf die entsprechenden Task-Instanzen verschoben.
- DSL-Parsing: Hier werden Build-Skripts ausgewertet und die verschiedenen Attribute der Android-DSL-Objekte aus dem
android-Block erstellt und festgelegt. Die in den folgenden Abschnitten beschriebenen Variant API-Callbacks werden ebenfalls in dieser Phase registriert. finalizeDsl(): Callback, mit dem Sie DSL-Objekte ändern können, bevor sie für die Erstellung von Komponenten (Varianten) gesperrt werden.VariantBuilder-Objekte werden anhand der in den DSL-Objekten enthaltenen Daten erstellt.DSL-Sperre: Die DSL ist jetzt gesperrt und es sind keine Änderungen mehr möglich.
beforeVariants(): Dieser Callback kann überVariantBuilderbeeinflussen, welche Komponenten erstellt werden, und einige ihrer Eigenschaften. Änderungen am Build-Ablauf und an den erstellten Artefakten sind weiterhin möglich.Varianten erstellen: Die Liste der Komponenten und Artefakte, die erstellt werden, ist jetzt endgültig und kann nicht mehr geändert werden.
onVariants(): In diesem Callback erhalten Sie Zugriff auf die erstelltenVariant-Objekte und können Werte oder Anbieter für die darin enthaltenenProperty-Werte festlegen, die verzögert berechnet werden sollen.Sperren von Varianten: Variantenobjekte sind jetzt gesperrt und Änderungen sind nicht mehr möglich.
Erstellte Aufgaben:
Variant-Objekte und ihreProperty-Werte werden verwendet, um dieTask-Instanzen zu erstellen, die für den Build erforderlich sind.
Das AGP führt eine AndroidComponentsExtension ein, mit der Sie Callbacks für finalizeDsl(), beforeVariants() und onVariants() registrieren können.
Die Erweiterung ist in Build-Skripts über den Block androidComponents verfügbar:
// This is used only for configuring the Android build through DSL.
android { ... }
// The androidComponents block is separate from the DSL.
androidComponents {
finalizeDsl { extension ->
...
}
}
Wir empfehlen jedoch, Build-Scripts nur für die deklarative Konfiguration mit der DSL des Android-Blocks zu verwenden und jede benutzerdefinierte imperative Logik in das Verzeichnis buildSrc oder in externe Plug-ins zu verschieben. In unserem GitHub-Repository mit Gradle-Anleitungen finden Sie auch buildSrc-Beispiele, die zeigen, wie Sie ein Plug-in in Ihrem Projekt erstellen. Achten Sie darauf, dass Ihr Plug-in in Ihrer Build-Datei mit dem gradle-api-Artefakt kompiliert wird. Hier ist ein Beispiel für die Registrierung der Callbacks aus dem Plug-in-Code:
abstract class ExamplePlugin: Plugin<Project> {
override fun apply(project: Project) {
val androidComponents = project.extensions.getByType(AndroidComponentsExtension::class.java)
androidComponents.finalizeDsl { extension ->
...
}
}
}
Sehen wir uns die verfügbaren Callbacks und die Arten von Anwendungsfällen, die Ihr Plug-in jeweils unterstützen kann, genauer an:
finalizeDsl(callback: (DslExtensionT) -> Unit)
In diesem Callback können Sie auf die DSL-Objekte zugreifen und sie ändern, die durch Parsen der Informationen aus dem android-Block in den Build-Dateien erstellt wurden.
Diese DSL-Objekte werden verwendet, um Varianten in späteren Phasen des Builds zu initialisieren und zu konfigurieren. Sie können beispielsweise programmatisch neue Konfigurationen erstellen oder Attribute überschreiben. Beachten Sie jedoch, dass alle Werte zum Zeitpunkt der Konfiguration aufgelöst werden müssen und daher nicht von externen Eingaben abhängen dürfen.
Nachdem dieser Callback ausgeführt wurde, sind die DSL-Objekte nicht mehr nützlich. Sie sollten keine Verweise mehr auf sie enthalten oder ihre Werte ändern.
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()
In dieser Phase des Builds erhalten Sie Zugriff auf VariantBuilder-Objekte, die die zu erstellenden Varianten und ihre Attribute bestimmen. Sie können beispielsweise bestimmte Varianten und ihre Tests programmatisch deaktivieren oder den Wert eines Attributs (z. B. minSdk) nur für eine ausgewählte Variante ändern. Ähnlich wie bei finalizeDsl() müssen alle von Ihnen angegebenen Werte zum Zeitpunkt der Konfiguration aufgelöst werden und dürfen nicht von externen Eingaben abhängen. Die VariantBuilder-Objekte dürfen nach Abschluss der Ausführung des beforeVariants()-Callbacks nicht mehr geändert werden.
androidComponents {
beforeVariants { variantBuilder ->
variantBuilder.minSdk = 23
}
}
Der beforeVariants()-Callback akzeptiert optional ein VariantSelector, das Sie über die Methode selector() für das androidComponentsExtension abrufen können. Damit können Sie Komponenten, die am Callback-Aufruf beteiligt sind, nach Name, Build-Typ oder Produktvariante filtern.
androidComponents {
beforeVariants(selector().withName("adfree")) { variantBuilder ->
variantBuilder.minSdk = 23
}
}
onVariants()
Wenn onVariants() aufgerufen wird, sind alle Artefakte, die vom AGP erstellt werden, bereits festgelegt. Sie können sie also nicht mehr deaktivieren. Sie können jedoch einige der für die Aufgaben verwendeten Werte ändern, indem Sie sie für Property-Attribute in den Variant-Objekten festlegen. Da die Property-Werte erst aufgelöst werden, wenn die AGP-Aufgaben ausgeführt werden, können Sie sie problemlos mit Anbietern aus Ihren eigenen benutzerdefinierten Aufgaben verknüpfen, die alle erforderlichen Berechnungen ausführen, einschließlich des Lesens aus externen Eingaben wie Dateien oder dem Netzwerk.
// 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() })
}
Generierte Quellen zum Build beitragen
Ihr Plug-in kann verschiedene Arten von generierten Quellen beisteuern, z. B.:
- Anwendungscode im Verzeichnis
java - Android-Ressourcen im Verzeichnis
res - Java-Ressourcen im Verzeichnis
resources - Android-Assets im Verzeichnis
assets
Eine vollständige Liste der Quellen, die Sie hinzufügen können, finden Sie in der Sources API.
Dieses Code-Snippet zeigt, wie Sie dem Java-Source-Set mit der Funktion addStaticSourceDirectory() einen benutzerdefinierten Quellordner namens ${variant.name} hinzufügen. Die Android-Toolchain verarbeitet dann diesen Ordner.
onVariants { variant ->
variant.sources.java?.let { java ->
java.addStaticSourceDirectory("custom/src/kotlin/${variant.name}")
}
}
Weitere Informationen finden Sie im addJavaSource-Rezept.
In diesem Code-Snippet wird gezeigt, wie Sie dem Source-Set res ein Verzeichnis mit Android-Ressourcen hinzufügen, die aus einer benutzerdefinierten Aufgabe generiert wurden. Der Vorgang ist für andere Quelltypen ähnlich.
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.
...
}
}
Weitere Informationen finden Sie im addCustomAsset-Rezept.
Auf Artefakte zugreifen und sie ändern
Mit dem AGP können Sie nicht nur einfache Eigenschaften der Variant-Objekte ändern, sondern auch einen Erweiterungsmechanismus nutzen, mit dem Sie Zwischen- und Endartefakte, die während des Builds erstellt werden, lesen oder transformieren können. Sie können beispielsweise den Inhalt der endgültigen, zusammengeführten AndroidManifest.xml-Datei in einem benutzerdefinierten Task lesen, um ihn zu analysieren, oder Sie können den Inhalt vollständig durch den Inhalt einer Manifestdatei ersetzen, die von Ihrem benutzerdefinierten Task generiert wurde.
Eine Liste der derzeit unterstützten Artefakte finden Sie in der Referenzdokumentation für die Klasse Artifact. Jeder Artefakttyp hat bestimmte Attribute, die nützlich sind:
Kardinalität
Die Kardinalität eines Artifact stellt die Anzahl der FileSystemLocation-Instanzen oder die Anzahl der Dateien oder Verzeichnisse des Artefakttyps dar. Informationen zur Kardinalität eines Artefakts erhalten Sie, wenn Sie die übergeordnete Klasse prüfen: Artefakte mit einer einzelnen FileSystemLocation sind eine Unterklasse von Artifact.Single. Artefakte mit mehreren FileSystemLocation-Instanzen sind eine Unterklasse von Artifact.Multiple.
FileSystemLocation Typ
Sie können prüfen, ob ein Artifact Dateien oder Verzeichnisse darstellt, indem Sie sich den parametrisierten FileSystemLocation-Typ ansehen. Dieser kann entweder ein RegularFile oder ein Directory sein.
Unterstützte Vorgänge
Jede Artifact-Klasse kann eine der folgenden Schnittstellen implementieren, um anzugeben, welche Vorgänge sie unterstützt:
Transformable: Ermöglicht die Verwendung einesArtifactals Eingabe für einenTask, der beliebige Transformationen daran vornimmt und eine neue Version desArtifactausgibt.Appendable: Gilt nur für Artefakte, die abgeleitete Klassen vonArtifact.Multiplesind. Das bedeutet, dass dieArtifactangehängt werden kann. Das heißt, ein benutzerdefinierterTaskkann neue Instanzen diesesArtifact-Typs erstellen, die der vorhandenen Liste hinzugefügt werden.Replaceable: Gilt nur für Artefakte, die abgeleitete Klassen vonArtifact.Singlesind. Ein ersetzbaresArtifactkann durch eine völlig neue Instanz ersetzt werden, die als Ausgabe einesTaskerzeugt wird.
Zusätzlich zu den drei Vorgängen zur Artefaktänderung unterstützt jedes Artefakt einen get()- (oder getAll())-Vorgang, der ein Provider mit der endgültigen Version des Artefakts zurückgibt (nachdem alle Vorgänge daran abgeschlossen sind).
Mehrere Plug-ins können über den Callback onVariants() beliebig viele Vorgänge für Artefakte in die Pipeline einfügen. Das AGP sorgt dafür, dass sie richtig verkettet werden, sodass alle Aufgaben zur richtigen Zeit ausgeführt und Artefakte korrekt erstellt und aktualisiert werden. Wenn also ein Vorgang Ausgaben durch Anhängen, Ersetzen oder Transformieren ändert, sieht der nächste Vorgang die aktualisierte Version dieser Artefakte als Eingaben usw.
Der Einstiegspunkt für die Registrierung von Vorgängen ist die Klasse Artifacts.
Das folgende Code-Snippet zeigt, wie Sie über ein Attribut des Variant-Objekts im onVariants()-Callback auf eine Instanz von Artifacts zugreifen können.
Anschließend können Sie Ihre benutzerdefinierte TaskProvider übergeben, um ein TaskBasedOperation-Objekt (1) zu erhalten, und es verwenden, um die Ein- und Ausgänge mit einer der wiredWith*-Methoden (2) zu verbinden.
Welche Methode Sie auswählen müssen, hängt von der Kardinalität und dem FileSystemLocation-Typ ab, der von der Artifact implementiert wird, die Sie transformieren möchten.
Schließlich übergeben Sie den Typ Artifact an eine Methode, die den ausgewählten Vorgang für das *OperationRequest-Objekt darstellt, das Sie als Antwort erhalten, z. B. toAppendTo(), toTransform() oder 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)
}
In diesem Beispiel ist MERGED_MANIFEST ein SingleArtifact und ein RegularFile. Daher müssen wir die Methode wiredWithFiles verwenden, die eine einzelne RegularFileProperty-Referenz für die Eingabe und eine einzelne RegularFileProperty-Referenz für die Ausgabe akzeptiert. Es gibt weitere wiredWith*-Methoden in der TaskBasedOperation-Klasse, die für andere Kombinationen von Artifact-Kardinalität und FileSystemLocation-Typen funktionieren.
Weitere Informationen zum Erweitern des AGP finden Sie in den folgenden Abschnitten des Gradle-Build-System-Handbuchs:
- Benutzerdefinierte Gradle-Plug-ins entwickeln
- Gradle-Plug-ins implementieren
- Developing Custom Gradle Task Types
- Lazy Configuration
- Vermeidung von Aufgabenkonfigurationen