Scrittura dei plug-in Gradle

Il plug-in Android per Gradle (AGP) è il sistema di build ufficiale per le applicazioni Android. Include il supporto per la compilazione di molti tipi diversi di origini e il loro collegamento in un'applicazione che puoi eseguire su un dispositivo Android fisico o su un emulatore.

AGP contiene punti di estensione per i plug-in per controllare gli input di build ed estendere la sua funzionalità tramite nuovi passaggi che possono essere integrati con le attività di build standard. Le versioni precedenti di AGP non avevano API ufficiali chiaramente separate dalle implementazioni interne. A partire dalla versione 7.0, AGP dispone di un insieme di API ufficiali e stabili su cui puoi fare affidamento.

Ciclo di vita dell'API AGP

AGP segue il ciclo di vita delle funzionalità di Gradle per designare lo stato delle sue API:

  • Interno: non destinato all'uso pubblico
  • In incubazione: disponibili per l'uso pubblico, ma non definitive, il che significa che potrebbero non essere compatibili con le versioni precedenti nella versione finale
  • Pubblico: disponibile per l'uso pubblico e stabile
  • Ritirata: non più supportata e sostituita con nuove API

Norme sul ritiro

AGP si sta evolvendo con il ritiro delle vecchie API e la loro sostituzione con nuove API stabili e un nuovo Domain Specific Language (DSL). Questa evoluzione riguarderà più release di AGP. Per saperne di più, consulta la cronologia della migrazione delle API/DSL di AGP.

Quando le API AGP vengono ritirate, per questa migrazione o per altri motivi, continueranno a essere disponibili nella release principale corrente, ma genereranno avvisi. Le API deprecate verranno rimosse completamente da AGP nella release principale successiva. Ad esempio, se un'API è deprecata in AGP 7.0, sarà disponibile in quella versione e genererà avvisi. Questa API non sarà più disponibile in AGP 8.0.

Per vedere esempi di nuove API utilizzate nelle personalizzazioni di build comuni, dai un'occhiata alle ricette del plug-in Android per Gradle. Forniscono esempi di personalizzazioni di build comuni. Puoi anche trovare ulteriori dettagli sulle nuove API nella nostra documentazione di riferimento.

Compila in base all'artefatto dell'API AGP

Per scrivere un plug-in Gradle personalizzato o una logica di build (ad esempio nella directory buildSrc, in un modulo di plug-in di convenzione o in un progetto di plug-in autonomo) che estende AGP, compila l'artefatto gradle-api di AGP anziché l'artefatto di implementazione del plug-in completo.

Perché eseguire la compilazione con l'artefatto gradle-api?

AGP pubblica due artefatti principali:

  • com.android.tools.build:gradle-api: contiene le API AGP ufficiali, incluse le API stabili (come AndroidComponentsExtension, Variant e le interfacce DSL pubbliche), le API in fase di incubazione (contrassegnate con @Incubating) e le API ritirate (contrassegnate con @Deprecated).
  • com.android.tools.build:gradle: contiene l'implementazione completa del runtime AGP, incluse le classi interne, le attività di build e le integrazioni del compilatore.

La compilazione con l'artefatto gradle-api offre diversi vantaggi chiave:

  • Evita le API interne: assicurati che il codice del plug-in acceda solo alle API ufficiali. In questo modo si evitano dipendenze accidentali dalle classi di implementazione interne (com.android.build.gradle.internal.*) che possono cambiare o essere rimosse nelle versioni di AGP senza preavviso.
  • Build più veloci e ingombro ridotto: l'artefatto gradle-api ha un albero delle dipendenze molto più piccolo rispetto all'artefatto gradle completo. In questo modo si riducono le dimensioni dei download, si evita l'inquinamento del classpath e si velocizza la compilazione della logica di build.
  • Isolamento del runtime: i plug-in personalizzati richiedono le definizioni dell'API solo durante la compilazione. In fase di runtime, l'implementazione completa dell'AGP viene fornita dalla build Android quando viene applicato il plug-in dell'app per Android o della libreria.

Aggiungi la dipendenza

Aggiungi l'artefatto gradle-api come dipendenza nel file di build del progetto del plug-in o nella directory buildSrc. L'artefatto gradle-api è pubblicato nel repository Maven di Google. Per risolvere l'artefatto, assicurati che la build includa il repository google(). Ad esempio, configuralo nel blocco dependencyResolutionManagement.repositories del file settings.gradle o settings.gradle.kts oppure nel blocco repositories del file di build del plug-in o del file di build della directory buildSrc.

Con un catalogo delle versioni

Se il tuo progetto utilizza un catalogo delle versioni, definisci la dipendenza gradle-api nel file 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" }

L'alias del catalogo android-gradle-plugin-api genera l'accessor con sicurezza dei tipi libs.android.gradle.plugin.api.

Poi, aggiungi libs.android.gradle.plugin.api al blocco dependencies del file di build del plug-in:

Kotlin

dependencies {
    compileOnly(libs.android.gradle.plugin.api)
}

Alla moda

dependencies {
    compileOnly libs.android.gradle.plugin.api
}

Senza un catalogo delle versioni

Se il tuo progetto non utilizza un catalogo delle versioni, dichiara la dipendenza direttamente nel file di build del plug-in:

Kotlin

dependencies {
    compileOnly("com.android.tools.build:gradle-api:9.4.0")
}

Alla moda

dependencies {
    compileOnly 'com.android.tools.build:gradle-api:9.4.0'
}

Utilizza l'opzione di configurazione compileOnly come predefinita per i plug-in autonomi destinati alla pubblicazione. Il progetto di consumo applica AGP e fornisce le sue classi di runtime nel classpath del buildscript. L'utilizzo della configurazione compileOnly impedisce al plug-in pubblicato di perdere una dipendenza di runtime su una versione AGP specifica ed evita conflitti di versione per i consumer. Se sviluppi una logica di build interna, ad esempio plug-in nella directory buildSrc o plug-in di convenzione nelle build composite, utilizza l'opzione di configurazione implementation. Questi plug-in vengono eseguiti all'interno della build e richiedono AGP nel relativo classpath di runtime.

Nozioni di base sulla build Gradle

Questa guida non copre l'intero sistema di build Gradle. Tuttavia, copre l'insieme minimo di concetti necessari per aiutarti a eseguire l'integrazione con le nostre API e rimanda alla documentazione principale di Gradle per ulteriori informazioni.

Presumiamo che tu abbia una conoscenza di base del funzionamento di Gradle, tra cui come configurare i progetti, modificare i file di build, applicare i plug-in ed eseguire le attività. Per scoprire le nozioni di base di Gradle in relazione ad AGP, ti consigliamo di consultare Configura la build. Per scoprire di più sul framework generale per la personalizzazione dei plug-in Gradle, consulta Sviluppo di plug-in Gradle personalizzati.

Glossario dei tipi lazy di Gradle

Gradle offre una serie di tipi che si comportano in modo "lazy" o che aiutano a rimandare calcoli complessi o la creazione di Task alle fasi successive della build. Questi tipi sono alla base di molte API Gradle e AGP. L'elenco seguente include i principali tipi di Gradle coinvolti nell'esecuzione lazy e i relativi metodi chiave.

Provider<T>
Fornisce un valore di tipo T (dove "T" indica qualsiasi tipo), che può essere letto durante la fase di esecuzione utilizzando get() o trasformato in un nuovo Provider<S> (dove "S" indica un altro tipo) utilizzando i metodi map(), flatMap() e zip(). Tieni presente che get() non deve mai essere chiamato durante la fase di configurazione.
  • map(): accetta una lambda e produce un Provider di tipo S, Provider<S>. L'argomento lambda di map() accetta il valore T e produce il valore S. La funzione lambda non viene eseguita immediatamente, ma la sua esecuzione viene posticipata al momento in cui viene chiamato get() sull'oggetto Provider<S> risultante, rendendo l'intera catena lazy.
  • flatMap(): accetta anche una funzione lambda e produce Provider<S>, ma la funzione lambda accetta un valore T e produce Provider<S> (anziché produrre direttamente il valore S). Utilizza flatMap() quando S non può essere determinato al momento della configurazione e puoi ottenere solo Provider<S>. In pratica, se hai utilizzato map() e hai ottenuto un tipo di risultato Provider<Provider<S>>, probabilmente avresti dovuto utilizzare flatMap().
  • zip(): consente di combinare due istanze Provider per produrre una nuova Provider, con un valore calcolato utilizzando una funzione che combina i valori delle due istanze Providers di input.
Property<T>
Implementa Provider<T>, quindi fornisce anche un valore di tipo T. A differenza di Provider<T>, che è di sola lettura, puoi anche impostare un valore per Property<T>. Puoi farlo in due modi:
  • Imposta un valore di tipo T direttamente quando è disponibile, senza la necessità di calcoli differiti.
  • Imposta un altro Provider<T> come origine del valore di Property<T>. In questo caso, il valore T viene materializzato solo quando viene chiamato Property.get().
TaskProvider
Implementa Provider<Task>. Per generare un TaskProvider, utilizza tasks.register() e non tasks.create(), per assicurarti che le attività vengano create solo in modalità lazy quando sono necessarie. Puoi utilizzare flatMap() per accedere agli output di un Task prima che Task venga creato, il che può essere utile se vuoi utilizzare gli output come input per altre istanze Task.

I provider e i relativi metodi di trasformazione sono essenziali per configurare gli input e gli output delle attività in modo lazy, ovvero senza la necessità di creare tutte le attività in anticipo e risolvere i valori.

I provider contengono anche informazioni sulla dipendenza delle attività. Quando crei un Provider trasformando un output Task, quel Task diventa una dipendenza implicita del Provider e verrà creato ed eseguito ogni volta che il valore del Provider viene risolto, ad esempio quando un altro Task lo richiede.

Ecco un esempio di registrazione di due attività, GitVersionTask e ManifestProducerTask, posticipando la creazione delle istanze Task fino a quando non sono effettivamente necessarie. Il valore di input ManifestProducerTask è impostato su un Provider ottenuto dall'output di GitVersionTask, pertanto ManifestProducerTask dipende implicitamente da 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))
    }

Queste due attività verranno eseguite solo se vengono richieste esplicitamente. Ciò può accadere nell'ambito di una chiamata Gradle, ad esempio se esegui ./gradlew debugManifestProducer o se l'output di ManifestProducerTask è collegato a un'altra attività e il suo valore diventa obbligatorio.

Anche se scriverai attività personalizzate che utilizzano input e/o producono output, AGP non offre l'accesso pubblico diretto alle proprie attività. Sono un dettaglio di implementazione soggetto a modifiche da una versione all'altra. Invece, AGP offre l'API Variant e l'accesso all'output delle sue attività o agli artefatti di build, che puoi leggere e trasformare. Per saperne di più, consulta API Variant, artefatti e attività in questo documento.

Fasi di build di Gradle

La creazione di un progetto è un processo intrinsecamente complicato e che richiede molte risorse. Esistono varie funzionalità, come l'evitamento della configurazione delle attività, i controlli di aggiornamento e la funzionalità di memorizzazione nella cache della configurazione, che aiutano a ridurre al minimo il tempo dedicato a calcoli riproducibili o non necessari.

Per applicare alcune di queste ottimizzazioni, gli script e i plug-in Gradle devono rispettare regole rigorose durante ciascuna delle diverse fasi di build di Gradle: inizializzazione, configurazione ed esecuzione. In questa guida ci concentreremo sulle fasi di configurazione ed esecuzione. Puoi trovare ulteriori informazioni su tutte le fasi nella guida al ciclo di vita delle build Gradle.

Fase di configurazione

Durante la fase di configurazione, vengono valutati gli script di build per tutti i progetti che fanno parte della build, vengono applicati i plug-in e vengono risolte le dipendenze della build. Questa fase deve essere utilizzata per configurare la build utilizzando oggetti DSL e per registrare le attività e i relativi input in modalità lazy.

Poiché la fase di configurazione viene sempre eseguita, indipendentemente dall'attività di cui è richiesta l'esecuzione, è particolarmente importante mantenerla snella e limitare i calcoli in modo che non dipendano da input diversi dagli script di build stessi. In altre parole, non devi eseguire programmi esterni, leggere dalla rete o eseguire calcoli lunghi che possono essere rimandati alla fase di esecuzione come istanze Task vere e proprie.

Fase di esecuzione

Nella fase di esecuzione, vengono eseguite le attività richieste e le relative attività dipendenti. In particolare, vengono eseguiti i metodi della classe Task contrassegnati con @TaskAction. Durante l'esecuzione dell'attività, puoi leggere dagli input (ad esempio i file) e risolvere i provider lazy chiamando Provider<T>.get(). La risoluzione dei provider lazy in questo modo avvia una sequenza di chiamate map() o flatMap() che seguono le informazioni sulla dipendenza delle attività contenute nel provider. Le attività vengono eseguite in modalità lazy per materializzare i valori richiesti.

API Variant, artefatti e attività

L'API Variant è un meccanismo di estensione del plug-in Android per Gradle che ti consente di manipolare le varie opzioni, normalmente impostate utilizzando il DSL nei file di configurazione di compilazione, che influiscono sulla build Android. L'API Variant ti consente anche di accedere agli artefatti intermedi e finali creati dalla build, come i file di classe, il manifest unito o i file APK/AAB.

Flusso di build Android e punti di estensione

Quando interagisci con AGP, utilizza punti di estensione appositamente creati anziché registrare i tipici callback del ciclo di vita di Gradle (come afterEvaluate()) o configurare dipendenze Task esplicite. Le attività create da AGP sono considerate dettagli di implementazione e non sono esposte come API pubblica. Devi evitare di provare a ottenere istanze degli oggetti Task o di indovinare i nomi Task e di aggiungere callback o dipendenze direttamente a questi oggetti Task.

AGP completa i seguenti passaggi per creare ed eseguire le sue istanze Task, che a loro volta producono gli artefatti di build. I passaggi principali della creazione di oggetti Variant sono seguiti da callback che ti consentono di apportare modifiche a determinati oggetti creati nell'ambito di una build. È importante notare che tutti i callback avvengono durante la fase di configurazione (descritta in questa pagina) e devono essere eseguiti rapidamente, rimandando qualsiasi lavoro complicato alle istanze Task appropriate durante la fase di esecuzione.

  1. Analisi del DSL: in questa fase vengono valutati gli script di build e vengono create e impostate le varie proprietà degli oggetti DSL Android dal blocco android. Anche i callback dell'API Variant descritti nelle sezioni seguenti vengono registrati durante questa fase.
  2. finalizeDsl(): callback che consente di modificare gli oggetti DSL prima che vengano bloccati per la creazione di componenti (varianti). Gli oggetti VariantBuilder vengono creati in base ai dati contenuti negli oggetti DSL.

  3. Blocco del DSL: il DSL è ora bloccato e non è più possibile apportare modifiche.

  4. beforeVariants(): questo callback può influenzare i componenti creati e alcune delle loro proprietà tramite VariantBuilder. Consente comunque di apportare modifiche al flusso di build e agli artefatti prodotti.

  5. Creazione delle varianti: l'elenco dei componenti e degli artefatti che verranno creati è ora definitivo e non può essere modificato.

  6. onVariants(): In questa callback, si ottiene l'accesso al creatoVariant oggetti e puoi impostare valori o fornitori per ilProperty valori che contengono, da calcolare in modo differito.

  7. Blocco delle varianti: gli oggetti delle varianti sono ora bloccati e non è più possibile apportare modifiche.

  8. Attività create: gli oggetti Variant e i relativi valori Property vengono utilizzati per creare le istanze Task necessarie per eseguire la build.

AGP introduce un AndroidComponentsExtension che ti consente di registrare i callback per finalizeDsl(), beforeVariants() e onVariants(). L'estensione è disponibile negli script di build tramite il blocco androidComponents:

// This is used only for configuring the Android build through DSL.
android { ... }

// The androidComponents block is separate from the DSL.
androidComponents {
   finalizeDsl { extension ->
      ...
   }
}

Tuttavia, ti consigliamo di mantenere gli script di build solo per la configurazione dichiarativa utilizzando il DSL del blocco android e di spostare qualsiasi logica imperativa personalizzata nella directory buildSrc o nei plug-in esterni. Puoi anche dare un'occhiata agli esempi di buildSrc nel nostro repository GitHub di ricette Gradle per scoprire come creare un plug-in nel tuo progetto. Assicurati che il plug-in venga compilato in base all'artefatto gradle-api nel file di build. Ecco un esempio di registrazione dei callback dal codice del plug-in:

abstract class ExamplePlugin: Plugin<Project> {

    override fun apply(project: Project) {
        val androidComponents = project.extensions.getByType(AndroidComponentsExtension::class.java)
        androidComponents.finalizeDsl { extension ->
            ...
        }
    }
}

Diamo un'occhiata più da vicino ai callback disponibili e al tipo di casi d'uso che il tuo plug-in può supportare in ciascuno di essi:

finalizeDsl(callback: (DslExtensionT) -> Unit)

In questo callback, puoi accedere e modificare gli oggetti DSL creati analizzando le informazioni del blocco android nei file di build. Questi oggetti DSL verranno utilizzati per inizializzare e configurare le varianti nelle fasi successive della build. Ad esempio, puoi creare in modo programmatico nuove configurazioni o eseguire l'override delle proprietà, ma tieni presente che tutti i valori devono essere risolti al momento della configurazione, quindi non devono fare affidamento su input esterni. Al termine dell'esecuzione di questo callback, gli oggetti DSL non sono più utili e non devi più conservare riferimenti o modificarne i valori.

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 questa fase della build, ottieni l'accesso agli oggetti VariantBuilder, che determinano le varianti che verranno create e le relative proprietà. Ad esempio, puoi disattivare in modo programmatico determinate varianti, i relativi test o modificare il valore di una proprietà (ad esempio, minSdk) solo per una variante scelta. Come per finalizeDsl(), tutti i valori forniti devono essere risolti al momento della configurazione e non dipendere da input esterni. Gli oggetti VariantBuilder non devono essere modificati una volta terminata l'esecuzione del callback beforeVariants().

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

Il callback beforeVariants() accetta facoltativamente un VariantSelector, che puoi ottenere tramite il metodo selector() su androidComponentsExtension. Puoi utilizzarlo per filtrare i componenti che partecipano alla chiamata di callback in base al nome, al tipo di build o alla versione prodotto.

androidComponents {
    beforeVariants(selector().withName("adfree")) { variantBuilder ->
        variantBuilder.minSdk = 23
    }
}

onVariants()

Quando viene chiamato onVariants(), tutti gli artefatti che verranno creati da AGP sono già stati decisi, quindi non puoi più disattivarli. Tuttavia, puoi modificare alcuni dei valori utilizzati per le attività impostandoli per gli attributi Property negli oggetti Variant. Poiché i valori Property verranno risolti solo quando vengono eseguite le attività di AGP, puoi collegarli in sicurezza ai provider dalle tue attività personalizzate che eseguiranno tutti i calcoli richiesti, inclusa la lettura da input esterni come file o la rete.

// 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() })
}

Contribuire con le origini generate alla build

Il tuo plug-in può contribuire ad alcuni tipi di origini generate, ad esempio:

Per un elenco completo delle origini che puoi aggiungere, consulta l'API Sources.

Questo snippet di codice mostra come aggiungere una cartella di origine personalizzata denominata ${variant.name} al set di risorse Java utilizzando la funzione addStaticSourceDirectory(). La toolchain Android elabora quindi questa cartella.

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

Per maggiori dettagli, consulta la ricetta addJavaSource.

Questo snippet di codice mostra come aggiungere una directory con risorse Android generate da un'attività personalizzata al set di risorse res. La procedura è simile per altri tipi di origine.

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.
      ...
   }
}

Per maggiori dettagli, consulta la ricetta addCustomAsset.

Accedi agli artefatti e modificali

Oltre a consentirti di modificare proprietà semplici negli oggetti Variant, AGP contiene anche un meccanismo di estensione che ti consente di leggere o trasformare gli artefatti intermedi e finali prodotti durante la build. Ad esempio, puoi leggere i contenuti finali del file AndroidManifest.xml unito in un Task personalizzato per analizzarli oppure puoi sostituire completamente i contenuti con quelli di un file manifest generato dal tuo Task personalizzato.

Puoi trovare l'elenco degli artefatti attualmente supportati nella documentazione di riferimento per la classe Artifact. Ogni tipo di artefatto ha determinate proprietà che è utile conoscere:

Cardinality

La cardinalità di un Artifact rappresenta il numero di istanze di FileSystemLocation o il numero di file o directory del tipo di artefatto. Puoi ottenere informazioni sulla cardinalità di un artefatto controllando la relativa classe principale: gli artefatti con un singolo FileSystemLocation saranno una sottoclasse di Artifact.Single; gli artefatti con più istanze FileSystemLocation saranno una sottoclasse di Artifact.Multiple.

Tipo di FileSystemLocation

Puoi verificare se un Artifact rappresenta file o directory esaminando il suo tipo FileSystemLocation parametrizzato, che può essere RegularFile o Directory.

Operazioni supportate

Ogni classe Artifact può implementare una delle seguenti interfacce per indicare quali operazioni supporta:

  • Transformable: consente di utilizzare un Artifact come input per un Task che esegue trasformazioni arbitrarie su di esso e genera una nuova versione del Artifact.
  • Appendable: si applica solo agli artefatti che sono sottoclassi di Artifact.Multiple. Ciò significa che è possibile aggiungere elementi a Artifact, ovvero un Task personalizzato può creare nuove istanze di questo tipo di Artifact che verranno aggiunte all'elenco esistente.
  • Replaceable: si applica solo agli artefatti che sono sottoclassi di Artifact.Single. Un Artifact sostituibile può essere sostituito da un'istanza completamente nuova, prodotta come output di un Task.

Oltre alle tre operazioni di modifica degli artefatti, ogni artefatto supporta un'operazione get() (o getAll()), che restituisce un Provider con la versione finale dell'artefatto (dopo che tutte le operazioni su di esso sono state completate).

Più plug-in possono aggiungere un numero qualsiasi di operazioni sugli artefatti nella pipeline dal callback onVariants() e AGP si assicurerà che siano concatenati correttamente in modo che tutte le attività vengano eseguite al momento giusto e gli artefatti vengano prodotti e aggiornati correttamente. Ciò significa che quando un'operazione modifica gli output aggiungendoli, sostituendoli o trasformandoli, l'operazione successiva vedrà la versione aggiornata di questi artefatti come input e così via.

L'entry point per la registrazione delle operazioni è la classe Artifacts. Il seguente snippet di codice mostra come accedere a un'istanza di Artifacts da una proprietà dell'oggetto Variant nel callback onVariants().

Puoi quindi passare il tuo TaskProvider personalizzato per ottenere un oggetto TaskBasedOperation (1) e utilizzarlo per collegare i suoi input e output utilizzando uno dei metodi wiredWith* (2).

Il metodo esatto da scegliere dipende dalla cardinalità e dal tipo di FileSystemLocation implementato dal Artifact che vuoi trasformare.

Infine, passi il tipo Artifact a un metodo che rappresenta l'operazione scelta sull'oggetto *OperationRequest che ricevi in cambio, ad esempio toAppendTo(), toTransform() o 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 questo esempio, MERGED_MANIFEST è un SingleArtifact ed è un RegularFile. Per questo motivo, dobbiamo utilizzare il metodo wiredWithFiles, che accetta un singolo riferimento RegularFileProperty per l'input e un singolo RegularFileProperty per l'output. Esistono altri metodi wiredWith* nella classe TaskBasedOperation che funzionano per altre combinazioni di cardinalità Artifact e tipi FileSystemLocation.

Per scoprire di più sull'estensione di AGP, ti consigliamo di leggere le seguenti sezioni del manuale del sistema di build Gradle: