Gradle-Plug-ins schreiben

Das Android-Gradle-Plug-in (AGP) ist das offizielle Build-System für Android-Apps. Sie unterstützt die Kompilierung vieler verschiedener Arten von Quellen und deren Verknüpfung in einer App, die Sie auf einem physischen Android-Gerät oder in einem Emulator ausführen können.

AGP enthält Erweiterungspunkte für Plug-ins, mit denen Build-Eingaben gesteuert und die Funktionalität durch neue Schritte erweitert werden kann, die in Standard-Build-Aufgaben eingebunden werden können. In früheren Versionen von AGP waren offizielle APIs nicht klar von internen Implementierungen getrennt. Ab Version 7.0 bietet AGP eine Reihe offizieller, stabiler APIs, auf die Sie sich verlassen können.

AGP API-Lebenszyklus

AGP folgt dem Lebenszyklus von Gradle-Features, um den Status seiner APIs festzulegen:

  • Intern: Nicht für die öffentliche Nutzung bestimmt
  • Inkubation: 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
  • Eingestellt: Wird nicht mehr unterstützt und durch neue APIs ersetzt

Einstellungsrichtlinie

AGP entwickelt sich weiter, da alte APIs eingestellt und durch neue, stabile APIs und eine neue anbieterspezifische Sprache (DSL) ersetzt werden. Diese Entwicklung wird mehrere AGP-Releases umfassen. Weitere Informationen finden Sie im Zeitplan für die Migration der AGP API/DSL.

Wenn AGP APIs eingestellt werden, sind sie bei dieser oder anderen Migration weiterhin in der aktuellen Hauptversion verfügbar, generieren aber Warnungen. Eingestellte APIs werden im nächsten Hauptrelease vollständig aus der AGP entfernt. Wenn eine API beispielsweise in AGP 7.0 eingestellt wird, ist sie in dieser Version verfügbar und es werden Warnungen ausgegeben. Diese API ist in AGP 8.0 nicht mehr verfügbar.

Beispiele für neue APIs, die bei 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 der Referenzdokumentation.

Gradle-Build-Grundlagen

In diesem Leitfaden wird nicht das gesamte Gradle-Buildsystem behandelt. Sie deckt jedoch die Mindestanforderungen an die Konzepte ab, die Sie für die Einbindung in unsere APIs benötigen. Außerdem enthält sie Links zur Hauptdokumentation von Gradle.

Wir gehen davon aus, dass Sie die Grundlagen der Funktionsweise von Gradle kennen, 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 Hinblick auf AGP finden Sie unter Build konfigurieren. Allgemeine Informationen zum Anpassen von Gradle-Plug-ins finden Sie unter Benutzerdefinierte Gradle-Plug-ins entwickeln.

Glossar zu Lazy-Typen in Gradle

Gradle bietet eine Reihe von Typen, die sich „träge“ verhalten oder die Ausführung umfangreicher Berechnungen oder die Erstellung von Task auf spätere Buildphasen verschieben. Diese Typen bilden die Grundlage vieler Gradle- und AGP-APIs. Die folgende Liste enthält die wichtigsten Gradle-Typen, die an der Lazy-Ausführung beteiligt sind, und ihre Schlüsselmethoden.

Provider<T>
Liefert einen Wert vom Typ T (wobei "T" einen beliebigen Typ bedeutet), der während der Ausführungsphase mit get() gelesen oder mit den Methoden map(), flatMap() und zip() in einen neuen Provider<S> umgewandelt werden kann (wobei "S" für einen anderen Typ steht). get() darf während der Konfigurationsphase nicht aufgerufen werden.
  • map(): Nimmt ein Lambda an und gibt eine Provider vom Typ S, Provider<S> zurück. Das Lambda-Argument für map() nimmt den Wert T an und gibt den Wert S zurück. Das Lambda wird nicht sofort ausgeführt, sondern erst, wenn get() auf das resultierende Provider<S> aufgerufen wird. Dadurch wird die gesamte Kette träge.
  • flatMap(): Akzeptiert auch eine Lambda-Funktion und erzeugt Provider<S>, aber sie nimmt den Wert T und erzeugt Provider<S> (anstatt den Wert S direkt zu erzeugen). Verwenden Sie flatMap(), wenn S bei der Konfiguration nicht ermittelt werden kann und Sie nur Provider<S> erhalten können. Wenn Sie map() verwendet haben und ein Ergebnis vom Typ Provider<Provider<S>> erhalten haben, sollten Sie stattdessen wahrscheinlich flatMap() verwenden.
  • zip(): Mit dieser Funktion können Sie zwei Provider-Instanzen kombinieren, um eine neue Provider zu erstellen. Der Wert wird mit einer Funktion berechnet, die die Werte der beiden Eingabe-Providers-Instanzen kombiniert.
Property<T>
implementiert Provider<T> und liefert daher auch einen Wert vom Typ T. Im Gegensatz zu Provider<T>, das nur lesbar ist, können Sie auch einen Wert für Property<T> festlegen. Dazu gibt es zwei Möglichkeiten:
  • Sie können einen Wert vom Typ T direkt festlegen, wenn er verfügbar ist, ohne dass verzögerte Berechnungen erforderlich sind.
  • Legen Sie eine andere Provider<T> als Quelle für den Wert der Property<T> fest. In diesem Fall wird der Wert T nur dann materialisiert, wenn Property.get() aufgerufen wird.
TaskProvider
Implementiert Provider<Task>. Verwenden Sie zum Generieren einer TaskProvider tasks.register() und nicht tasks.create(), damit Aufgaben nur dann lazily instanziiert werden, wenn sie benötigt werden. Mit flatMap() können Sie auf die Ausgaben einer Task zugreifen, bevor die Task erstellt wird. Das kann nützlich sein, wenn Sie die Ausgaben als Eingaben für andere Task-Instanzen verwenden möchten.

Anbieter und ihre Transformationsmethoden sind wichtig, um Ein- 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 eine Provider durch Transformieren einer Task-Ausgabe erstellen, wird diese Task zu einer impliziten Abhängigkeit der Provider. Sie wird erstellt und ausgeführt, wenn der Wert der Provider aufgelöst wird, z. B. wenn sie für eine andere Task erforderlich ist.

Hier ein Beispiel für die Registrierung von zwei Aufgaben, GitVersionTask und ManifestProducerTask, wobei die Erstellung der Task-Instanzen bis zur tatsächlichen Notwendigkeit verschoben wird. Der Eingabewert für ManifestProducerTask wird auf einen Provider festgelegt, der aus der Ausgabe von GitVersionTask stammt. ManifestProducerTask hängt also 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 ausdrücklich angefordert werden. Das kann bei einer Gradle-Aufruf erfolgen, z. B. wenn Sie ./gradlew debugManifestProducer ausführen oder die Ausgabe von ManifestProducerTask mit einer anderen Aufgabe verknüpft ist und deren Wert erforderlich wird.

Sie werden zwar benutzerdefinierte Aufgaben schreiben, die Eingaben verarbeiten und/oder Ausgaben erzeugen, aber AGP bietet keinen direkten öffentlichen Zugriff auf seine eigenen Aufgaben. Sie sind eine Implementierungsangelegenheit, die sich von Version zu Version ändern kann. Stattdessen bietet AGP die Variant API und Zugriff auf die Ausgabe der Aufgaben oder Build-Artefakte, die Sie lesen und transformieren können. Weitere Informationen finden Sie in diesem Dokument unter Variant API, Artifacts, and Tasks (Variant API, Artefakte und Aufgaben).

Gradle-Build-Phasen

Das Erstellen eines Projekts ist von Natur aus ein komplexer und ressourcenintensiver Prozess. Es gibt verschiedene Funktionen wie die Vermeidung der Aufgabenkonfiguration, Aktualisierungsüberprüfungen und die Konfigurations-Caching-Funktion, mit denen sich die Zeit für reproduzierbare oder unnötige Berechnungen minimieren lässt.

Damit einige dieser Optimierungen angewendet werden können, müssen Gradle-Scripts und ‑Plug-ins in jeder der drei Gradle-Buildphasen (Initialisierung, Konfiguration und Ausführung) strengen Regeln folgen. 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 sollten Sie den Build mit DSL-Objekten konfigurieren und Aufgaben und ihre Eingaben verzögert registrieren.

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 alle Berechnungen darauf zu beschränken, dass sie nur von den Build-Scripts selbst abhängen. Das bedeutet, dass Sie keine externen Programme ausführen, Daten aus dem Netzwerk lesen oder lange Berechnungen ausführen sollten, die als Task-Instanzen in 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 Aufgabenausführung können Sie Eingaben (z. B. Dateien) lesen und Lazy-Anbieter durch Aufrufen von Provider<T>.get() auflösen. Wenn Sie Lazy-Anbieter auf diese Weise auflösen, wird eine Abfolge von map()- oder flatMap()-Aufrufen gestartet, die den im Anbieter enthaltenen Informationen zu Aufgabenabhängigkeiten folgen. Aufgaben werden verzögert ausgeführt, um die erforderlichen Werte zu materialisieren.

Variant API, Artefakte und Aufgaben

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. Die Variant API bietet Ihnen außerdem Zugriff auf Zwischen- und Endartefakte, die durch den 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 AGP spezielle 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 freigegeben. Sie dürfen es vermeiden, Instanzen der Task-Objekte abzurufen oder die Task-Namen zu erraten und Callbacks oder Abhängigkeiten direkt zu diesen Task-Objekten hinzuzufügen.

AGP führt die folgenden Schritte aus, um seine Task-Instanzen zu erstellen und auszuführen, die wiederum die Build-Artefakte generieren. Auf die wichtigsten Schritte beim Erstellen von Variant-Objekten folgen Callbacks, mit denen Sie Änderungen an bestimmten Objekten vornehmen können, die im Rahmen eines Builds erstellt wurden. Alle Rückrufe werden während der Konfigurationsphase (auf dieser Seite beschrieben) ausgeführt und müssen schnell ausgeführt werden. Komplizierte Aufgaben werden stattdessen während der Ausführungsphase an die entsprechenden Task-Instanzen weitergeleitet.

  1. DSL-Parsing: Dies ist der Zeitpunkt, an dem Build-Skripts ausgewertet und die verschiedenen Attribute der Android-DSL-Objekte aus dem Block android erstellt und festgelegt werden. In dieser Phase werden auch die in den folgenden Abschnitten beschriebenen Variant API-Callbacks registriert.
  2. 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 Daten in den DSL-Objekten erstellt.

  3. DSL-Sperre: DSL ist jetzt gesperrt und Änderungen sind nicht mehr möglich.

  4. beforeVariants(): Über diesen Rückruf können Sie über VariantBuilder beeinflussen, welche Komponenten erstellt werden und welche Eigenschaften sie haben. Änderungen am Build-Ablauf und an den erzeugten Artefakten sind weiterhin möglich.

  5. Variantenerstellung: Die Liste der zu erstellenden Komponenten und Artefakte ist jetzt abgeschlossen und kann nicht mehr geändert werden.

  6. onVariants(): In diesem Callback erhalten Sie Zugriff auf die erstellten Variant-Objekte und können Werte oder Anbieter für die darin enthaltenen Property-Werte festlegen, die verzögert berechnet werden sollen.

  7. Gesperrte Varianten: Variantenobjekte sind jetzt gesperrt und Änderungen sind nicht mehr möglich.

  8. Erstellte Aufgaben: Mit Variant-Objekten und ihren Property-Werten werden die Task-Instanzen erstellt, die für die Ausführung des Builds erforderlich sind.

AGP führt die AndroidComponentsExtension ein, mit der du Callbacks für finalizeDsl(), beforeVariants() und onVariants() registrieren kannst. Die Erweiterung ist in Build-Scripts ü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 benutzerdefinierte imperative Logik in buildSrc oder externe Plug-ins zu verschieben. In den buildSrc-Beispielen im GitHub-Repository für Gradle-Rezepte erfahren Sie, wie Sie ein Plug-in in Ihrem Projekt erstellen. 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 Anwendungsfälle an, die dein Plug-in in jedem Fall unterstützen kann:

finalizeDsl(callback: (DslExtensionT) -> Unit)

In diesem Callback können Sie auf die erstellten DSL-Objekte zugreifen und diese ändern. Dazu parsen Sie die Informationen aus dem Block android in den Build-Dateien. 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, sodass sie nicht auf externen Eingaben basieren dürfen. Nach Abschluss der Ausführung dieses Callbacks sind die DSL-Objekte nicht mehr nützlich. Sie sollten keine Verweise mehr darauf speichern 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 Eigenschaften bestimmen. So können Sie beispielsweise bestimmte Varianten und ihre Tests programmatisch deaktivieren oder den Wert einer Property (z. B. minSdk) nur für eine ausgewählte Variante ändern. Ähnlich wie bei finalizeDsl() müssen alle angegebenen Werte bei der Konfiguration aufgelöst werden und dürfen nicht von externen Eingaben abhängen. Die VariantBuilder-Objekte dürfen nicht mehr geändert werden, nachdem die Ausführung des beforeVariants()-Callbacks abgeschlossen ist.

androidComponents {
    beforeVariants { variantBuilder ->
        variantBuilder.minSdk = 23
    }
}

Der beforeVariants()-Callback verwendet optional einen VariantSelector, den du über die selector()-Methode in androidComponentsExtension abrufen kannst. Sie können damit Komponenten, die an der Rückrufaufruf-Funktion teilnehmen, 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 von 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 bei der Ausführung der AGP-Aufgaben aufgelöst werden, können Sie sie sicher 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

Das Plug-in kann verschiedene Arten generierter Quellen bereitstellen, z. B.:

Eine vollständige Liste der Quellen, die Sie hinzufügen können, finden Sie in der Sources API.

In diesem Code-Snippet wird gezeigt, wie Sie dem Java-Quellsatz mithilfe der Funktion addStaticSourceDirectory() einen benutzerdefinierten Quellordner namens ${variant.name} hinzufügen. Dieser Ordner wird dann von der Android-Toolchain verarbeitet.

onVariants { variant ->
    variant.sources.java?.let { java ->
        java.addStaticSourceDirectory("custom/src/kotlin/${variant.name}")
    }
}

Weitere Informationen finden Sie im Rezept addJavaSource.

In diesem Code-Snippet wird gezeigt, wie Sie dem res-Quellsatz ein Verzeichnis mit Android-Ressourcen hinzufügen, das aus einer benutzerdefinierten Aufgabe generiert wurde. 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 Rezept addCustomAsset.

Auf Artefakte zugreifen und sie ändern

Mit AGP können Sie nicht nur einfache Eigenschaften der Variant-Objekte ändern, sondern auch Zwischen- und Endartefakte lesen oder transformieren, die während des Builds erstellt wurden. Sie können beispielsweise den endgültigen, zusammengeführten Inhalt der Datei AndroidManifest.xml in einer benutzerdefinierten Task lesen, um ihn zu analysieren, oder 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 Eigenschaften, die nützlich sind:

Kardinalität

Die Kardinalität eines Artifact entspricht der Anzahl der FileSystemLocation-Instanzen oder der Anzahl der Dateien oder Verzeichnisse des Artefakttyps. Informationen zur Kardinalität eines Artefakts erhalten Sie, indem Sie die zugehörige übergeordnete Klasse prüfen: Artefakte mit einer einzelnen FileSystemLocation sind eine abgeleitete Klasse von Artifact.Single. Artefakte mit mehreren FileSystemLocation-Instanzen sind eine abgeleitete Klasse von Artifact.Multiple.

FileSystemLocation Typ

Ob ein Artifact Dateien oder Verzeichnisse darstellt, lässt sich anhand des parametrisierten FileSystemLocation-Typs erkennen. Dieser kann entweder RegularFile oder Directory sein.

Unterstützte Vorgänge

Jede Artifact-Klasse kann eine der folgenden Schnittstellen implementieren, um anzugeben, welche Vorgänge sie unterstützen:

  • Transformable: Ermöglicht es, einen Artifact als Eingabe für einen Task zu verwenden, der beliebige Transformationen darauf ausführt und eine neue Version des Artifact ausgibt.
  • Appendable: Gilt nur für Artefakte, die Unterklassen von Artifact.Multiple sind. Das bedeutet, dass das Artifact an eine benutzerdefinierte Task angehängt werden kann, sodass neue Instanzen dieses Artifact-Typs erstellt werden können, die der vorhandenen Liste hinzugefügt werden.
  • Replaceable: Gilt nur für Artefakte, die abgeleitete Klassen von Artifact.Single sind. Eine austauschbare Artifact kann durch eine vollständig neue Instanz ersetzt werden, die als Ausgabe einer Task erzeugt wird.

Zusätzlich zu den drei Vorgängen zur Änderung von Artefakten unterstützt jedes Artefakt einen get()- (oder getAll()-)Vorgang, der einen Provider mit der endgültigen Version des Artefakts zurückgibt (nachdem alle Vorgänge darauf abgeschlossen sind).

Mehrere Plug-ins können der Pipeline über den onVariants()-Callback beliebig viele Vorgänge für Artefakte hinzufügen. AGP sorgt dafür, dass sie richtig verkettet werden, damit alle Aufgaben zur richtigen Zeit ausgeführt und Artefakte korrekt erstellt und aktualisiert werden. Das bedeutet, dass bei einem Vorgang, bei dem Ausgaben durch Anhängen, Ersetzen oder Transformieren geändert werden, die aktualisierte Version dieser Artefakte als Eingaben für den nächsten Vorgang verwendet wird usw.

Der Einstiegspunkt für die Registrierung von Vorgängen ist die Klasse Artifacts. Im folgenden Code-Snippet wird gezeigt, wie du im onVariants()-Callback über eine Eigenschaft des Variant-Objekts auf eine Instanz von Artifacts zugreifen kannst.

Sie können dann Ihre benutzerdefinierte TaskProvider übergeben, um ein TaskBasedOperation-Objekt (1) zu erhalten, und damit die Eingaben und Ausgaben mit einer der wiredWith*-Methoden (2) 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 Artifact-Typ an eine Methode, die den ausgewählten Vorgang auf dem zurückgegebenen *OperationRequest-Objekt darstellt, 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 eine SingleArtifact und eine RegularFile. Daher müssen wir die Methode wiredWithFiles verwenden, die eine einzelne RegularFileProperty-Referenz für die Eingabe und eine einzelne RegularFileProperty für die Ausgabe akzeptiert. Es gibt weitere wiredWith*-Methoden in der TaskBasedOperation-Klasse, die für andere Kombinationen von Artifact-Kardinalen und FileSystemLocation-Typen geeignet sind.

Weitere Informationen zum Erweitern von AGP finden Sie in den folgenden Abschnitten des Handbuchs des Gradle-Build-Systems: