Il plug-in Android Gradle (AGP) è il sistema di compilazione ufficiale per Android diverse applicazioni. Supporta la compilazione di molti tipi diversi di fonti e di collegarli tra loro in un'applicazione che puoi eseguire su un Dispositivo Android o un emulatore.
AGP contiene punti di estensione per consentire ai plug-in di controllare gli input della build ed estendere i propri la tua funzionalità attraverso nuovi passaggi che possono essere integrati con attività di machine learning. Le versioni precedenti di AGP non avevano API ufficiali chiaramente separate da implementazioni interne. A partire dalla versione 7.0, AGP ha un insieme API stabili ufficiali su cui puoi fare affidamento attiva.
Ciclo di vita dell'API AGP
AGP segue il ciclo di vita delle funzionalità Gradle per indicare lo stato delle sue API:
- Interno: non destinato all'uso pubblico
- Incubazione: disponibile per uso pubblico, ma non definitiva, il che significa che potrebbe non essere compatibile con le versioni precedenti nella versione finale
- Pubblico: disponibile per uso pubblico e stabile
- Obsoleta: non più supportata e sostituita con nuove API
Norme sul ritiro
AGP si sta evolvendo con il ritiro delle vecchie API e dei loro la sostituzione con nuove API stabili e un nuovo Domain Specific Language (DSL). Questa evoluzione interesserà diverse release AGP e puoi scoprire di più al riguardo in base alle tempistiche della migrazione di API AGP/DSL.
Quando le API AGP vengono deprecate, per questa migrazione o per altri scopi, continuerà a essere disponibile nella release principale attuale, ma genererà avvisi. Le API deprecate verranno completamente rimosse da AGP nelle . Ad esempio, se un'API è deprecata in AGP 7.0, sarà disponibile in quella versione e generare avvisi. L'API non sarà più disponibile in AGP 8.0.
Per vedere esempi di nuove API utilizzate nelle personalizzazioni comuni delle build, dai un'occhiata nelle ricette del plug-in Android per Gradle. Forniscono esempi di personalizzazioni comuni delle build. Puoi anche trovare altre sulle nuove API nel nostro documentazione di riferimento.
Nozioni di base sulla build di Gradle
Questa guida non tratta l'intero sistema di build di Gradle. Tuttavia, copre l'insieme minimo di concetti necessari per aiutarti all'integrazione con le nostre API e link alla documentazione principale di Gradle per ulteriori letture.
Partiamo dal presupposto che tu abbia le conoscenze di base del funzionamento di Gradle, compresa la configurazione progetti, modificare i file di build, applicare plug-in ed eseguire attività. Per saperne di più di Gradle in relazione ad AGP, ti consigliamo di consultare Configurare . Per conoscere il framework generale per la personalizzazione, Plug-in Gradle, consulta Sviluppo di plug-in Gradle personalizzati.
Glossario dei tipi lenti Gradle
Gradle offre vari tipi di comportamenti "pigra", o contribuiscono a rimandare in modo intensivo
di dati
Task
dalla creazione alle fasi successive della build. Questi tipi sono al centro di molte
API Gradle e AGP. Il seguente elenco include i principali tipi di Gradle coinvolti
l'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 esecuzioneget()
: o trasformato in un nuovoProvider<S>
(dove "S" indica un altro tipo) utilizzando i metodimap()
,flatMap()
ezip()
. Tieni presente cheget()
deve non verranno mai chiamati durante la fase di configurazione.map()
: accetta un lambda e produce unProvider
di tipoS
,Provider<S>
. L'argomento lambda amap()
prende il valoreT
e produce il valoreS
. La funzione lambda non viene eseguita immediatamente; la sua esecuzione viene differito al momento in cuiget()
viene chiamato nell'elementoProvider<S>
risultante, rendendo l'intera catena pigra.flatMap()
: accetta anche una lambda e produceProvider<S>
, ma lambda prende un valoreT
e produceProvider<S>
(anziché produrre il valore il valoreS
direttamente). Usa flatMap() quando non è possibile determinare S in per la configurazione e potrai ottenere soloProvider<S>
. In pratica parlando, se hai usatomap()
e alla fine hai ottenuto unProvider<Provider<S>>
di risultato, probabilmente avresti dovuto usareflatMap()
.zip()
: Consente di combinare due istanzeProvider
per produrre una nuovaProvider
, con un valore calcolato utilizzando una funzione che combina i valori dalle due istanzeProviders
di input.
Property<T>
- Implementa
Provider<T>
, quindi fornisce anche un valore di tipoT
. Non mi piace conProvider<T>
, che è di sola lettura, puoi anche impostare un valore per il parametroProperty<T>
. Puoi farlo in due modi:- Imposta un valore di tipo
T
direttamente quando è disponibile, senza bisogno di calcoli differiti. - Imposta un altro
Provider<T>
come origine del valore diProperty<T>
. Nella In questo caso, il valoreT
viene materializzato solo quandoProperty.get()
è chiamato.
- Imposta un valore di tipo
TaskProvider
- Implementa
Provider<Task>
. Per generare unTaskProvider
, usatasks.register()
e nontasks.create()
, per garantire che venga creata un'istanza solo per le attività con pigrizia quando serve. Puoi utilizzareflatMap()
per accedere agli output di unTask
prima della creazione diTask
; questo può essere utile se vuoi utilizzare gli output come input per altre istanzeTask
.
I provider e i relativi metodi di trasformazione sono essenziali per impostare gli input e output di attività in modo lento, ovvero senza la necessità creare preventivamente tutte le attività e risolvere i valori.
I provider includono anche informazioni sulle dipendenze delle attività. Quando crei un Provider
di
trasformando un output Task
, Task
diventa una dipendenza implicita
Provider
e verrà creato ed eseguito ogni volta che il valore dell'attributo Provider
sarà
venga risolto, ad esempio quando un altro Task
lo richiede.
Di seguito è riportato un esempio di registrazione di due attività, GitVersionTask
e
ManifestProducerTask
, rinviando la creazione delle istanze Task
fino a
sono effettivamente richieste. Il valore di input ManifestProducerTask
è impostato su un
Provider
ottenuto dall'output di GitVersionTask
, quindi
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à vengono eseguite solo se vengono richieste esplicitamente. Questo può
avvengono come parte di una chiamata Gradle, ad esempio se esegui ./gradlew
debugManifestProducer
o se l'output di ManifestProducerTask
è connesso
a un'altra attività e il suo valore diventa obbligatorio.
Mentre scriverai attività personalizzate che consumano input e/o producono output, AGP non offre l'accesso pubblico direttamente alle proprie attività. Si tratta di un i dettagli dell'implementazione sono soggetti a modifiche da una versione all'altra. Invece, AGP offre l'API Variant e l'accesso all'output delle sue attività, oppure build artefatti, che puoi leggere e trasformare. Consulta API Variant, artefatti e attività in questo per ulteriori informazioni.
Fasi di creazione di Gradle
La realizzazione di un progetto è intrinsecamente un processo complicato e che richiede risorse e ci sono varie funzionalità, come evitare la configurazione delle attività, aggiornare dei controlli e la funzionalità di memorizzazione nella cache della configurazione, che aiuta a ridurre al minimo il tempo su calcoli riproducibili o non necessari.
Per applicare alcune di queste ottimizzazioni, gli script e i plug-in Gradle devono rispetta rigide regole durante ciascuna delle diverse fasi di creazione di Gradle: inizializzazione, configurazione ed esecuzione. In questa guida, ci concentreremo le fasi di configurazione ed esecuzione. Puoi trovare ulteriori informazioni su tutti le fasi nella guida al ciclo di vita dello sviluppo di Gradle.
Fase di configurazione
Durante la fase di configurazione, gli script di build per tutti i progetti che fanno parte della build vengono valutati, i plug-in vengono applicati e le dipendenze risolto. Questa fase deve essere utilizzata per configurare la build utilizzando oggetti DSL e per registrare le attività e i relativi input in modo lento.
Poiché la fase di configurazione viene sempre eseguita, indipendentemente dall'attività
richiesta di esecuzione, è particolarmente importante mantenerla snella e limitare
i calcoli dipendono da input diversi dagli stessi script di build.
Vale a dire che non devi eseguire programmi esterni o leggere dalla rete.
eseguire calcoli lunghi che possono essere rinviati alla fase di esecuzione come
Task
istanza.
Fase di esecuzione
Nella fase di esecuzione, le attività richieste e le attività dipendenti
eseguito. Nello specifico, i metodi della classe Task
contrassegnati con @TaskAction
sono
eseguito. Durante l'esecuzione dell'attività, è possibile leggere dagli input (come
file) e risolvere i problemi dei provider lazy chiamando Provider<T>.get()
. Risoluzione dei problemi di pigrizia
In questo modo, viene avviata una sequenza di chiamate map()
o flatMap()
che seguono
le informazioni sulle dipendenze dell'attività contenute nel provider. Le attività vengono eseguite
per materializzare i valori richiesti.
API Variant, artefatti e attività
L'API Variant è un meccanismo di estensione nel plug-in Android Gradle che consente Puoi modificare le varie opzioni, solitamente impostate utilizzando la connessione DSL nella build di configurazione di Android, che influenzano la build di Android. Anche l'API Variant consente di accedere agli artefatti intermedi e finali creati build, ad esempio i file dei corsi, i file manifest uniti o i file APK/AAB.
Flusso di build Android e punti di estensione
Quando interagisci con AGP, usa punti di estensione creati appositamente
della registrazione dei tipici callback del ciclo di vita di Gradle (ad esempio afterEvaluate()
) o
configurare dipendenze Task
esplicite. Le attività create da AGP sono considerate
dettagli dell'implementazione e non sono esposti come API pubblica. Devi evitare
di recuperare istanze degli oggetti Task
o di indovinare i nomi Task
e
aggiungendo 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 della build. I passaggi principali
Variant
la creazione di oggetti è seguita da callback che consentono di apportare modifiche
di oggetti creati come parte di una build. È importante notare che tutte le
vengono eseguiti durante la fase di configurazione
(descritti in questa pagina) e devono essere veloci, rinviando le attività complesse
alle istanze Task
corrette durante la fase di esecuzione.
- Analisi DSL: durante la valutazione degli script di build e quando
vengono create varie proprietà degli oggetti Android DSL dal blocco
android
per iniziare. Anche i callback dell'API Variant descritti nelle sezioni seguenti vengono registrati durante questa fase. finalizeDsl()
: Callback che consente di modificare gli oggetti DSL prima che vengano bloccato per la creazione del componente (variante).VariantBuilder
oggetti creati in base ai dati contenuti negli oggetti DSL.Blocco DSL: ora la rete DSL è bloccata e non è più possibile apportare modifiche.
beforeVariants()
: questo callback può influire sui componenti creati, e alcune delle loro proprietà, tramiteVariantBuilder
. Consente comunque modifiche al flusso di build e agli artefatti che vengono prodotti.Creazione della variante: l'elenco dei componenti e degli artefatti che verranno creato è ora finalizzato e non può essere modificato.
onVariants()
: In questo callback, puoi accedere all'elementoVariant
creato e puoi impostare valori o provider per i valoriProperty
che che devono essere calcolati con calma.Blocco delle varianti: gli oggetti delle varianti sono ora bloccati e le modifiche non sono più. possibile.
Attività create:
Variant
oggetti e i relativi valoriProperty
vengono utilizzati per e creerai le istanzeTask
necessarie per eseguire la build.
AGP introduce
AndroidComponentsExtension
che ti consente
registri 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, il nostro consiglio è di mantenere gli script di build solo per
la configurazione usando lo standard DSL del blocco Android
sposta qualsiasi logica imperativa personalizzata in buildSrc
o plug-in esterni. Puoi anche consultare il buildSrc
esempi nel nostro repository GitHub di ricette per Gradle per scoprire come creare un plug-in nel tuo progetto. 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 supportati dal plug-in in ognuno di essi:
finalizeDsl(callback: (DslExtensionT) -> Unit)
Con questo callback, puoi accedere e modificare gli oggetti DSL che
creato analizzando le informazioni del blocco android
nei file di build.
Questi oggetti DSL saranno utilizzati per inizializzare e configurare le varianti in un secondo momento
fasi della build. Ad esempio, puoi creare in modo programmatico nuovi
configurazioni o di override delle proprietà, ma tieni presente che tutti i valori devono essere
risolti in fase di configurazione, quindi non devono fare affidamento su input esterni.
Al termine dell'esecuzione del callback, gli oggetti DSL non sono più utili e
non devi più contenere riferimenti a questi elementi 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, puoi accedere agli oggetti VariantBuilder
, che
determinano le varianti che verranno create e le relative proprietà. Ad esempio:
puoi disattivare in modo programmatico determinate varianti e i relativi test oppure modificare un
(ad es. minSdk
) solo per una variante scelta. Simile a
finalizeDsl()
, tutti i valori forniti devono essere risolti in fase di configurazione
molto tempo e non dipendono da input esterni. Gli oggetti VariantBuilder
non devono essere
modificato al termine dell'esecuzione del callback beforeVariants()
.
androidComponents {
beforeVariants { variantBuilder ->
variantBuilder.minSdk = 23
}
}
Il callback beforeVariants()
accetta facoltativamente un valore VariantSelector
, che puoi
ottenibile tramite il metodo selector()
su androidComponentsExtension
. Puoi
utilizzala per filtrare i componenti che partecipano alla chiamata del callback in base a
il nome, il tipo di build
o la varietà del prodotto.
androidComponents {
beforeVariants(selector().withName("adfree")) { variantBuilder ->
variantBuilder.minSdk = 23
}
}
onVariants()
Quando verrà chiamato onVariants()
, tutti gli artefatti che verranno creati
Le norme AGP sono già state decise, quindi non puoi più disabilitarle. Tuttavia, puoi
modificare alcuni dei valori utilizzati per le attività impostandoli per
Attributi Property
negli oggetti Variant
. Poiché i valori Property
risolte solo quando le attività di AGP vengono eseguite, puoi tranquillamente collegarle
delle tue attività personalizzate, che eseguiranno tutte le operazioni richieste
come la lettura da input esterni come file o 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 alle origini generate per la build
Il plug-in può contribuire con alcuni tipi di origini generate, ad esempio:
- Codice dell'applicazione nella directory
java
- Risorse Android nel
Directory
res
- Risorse Java
nella directory
resources
- Asset Android nel
Directory
assets
Per l'elenco completo delle fonti che puoi aggiungere, consulta API Sources.
Questo snippet di codice mostra come aggiungere una cartella di origine personalizzata
${variant.name}
al set di origini Java utilizzando addStaticSourceDirectory()
personalizzata. La Toolchain di Android elabora quindi questa cartella.
onVariants { variant ->
variant.sources.java?.let { java ->
java.addStaticSourceDirectory("custom/src/kotlin/${variant.name}")
}
}
Consulta la ricetta addJavaSource per ulteriori informazioni.
Questo snippet di codice mostra come aggiungere una directory con risorse Android
generate da un'attività personalizzata nel set di origini res
. Il processo è 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.
...
}
}
Consulta la ricetta addCustomAsset per ulteriori informazioni.
Accesso e modifica degli artefatti
Oltre a consentirti di modificare semplici proprietà degli oggetti Variant
, AGP
contiene anche un meccanismo di estensione che consente di leggere o trasformare
gli artefatti intermedi e finali prodotti durante la creazione. Ad esempio, puoi
leggi i contenuti finali del file AndroidManifest.xml
uniti in un file Task
personalizzato in
analizzarlo o sostituirne del tutto il contenuto con quello di un file manifest
generate dalla tua Task
personalizzata.
Puoi trovare l'elenco degli artefatti attualmente supportati nel riferimento
documentazione per la classe Artifact
. Ogni tipo di artefatto ha determinate proprietà che è utile conoscere:
Cardinalità
La cardinalità di un Artifact
rappresenta il suo numero di FileSystemLocation
o il numero di file o directory del tipo di artefatto. Puoi
ottenere informazioni sulla cardinalità di un artefatto controllandone l'elemento padre
class: gli artefatti con un solo FileSystemLocation
saranno una sottoclasse
Artifact.Single
gli artefatti con più istanze FileSystemLocation
essere una sottoclasse di Artifact.Multiple
.
FileSystemLocation
tipo
Per verificare se un Artifact
rappresenta file o directory, osserva la sua
di tipo FileSystemLocation
con parametri, che può essere RegularFile
o
Directory
.
Operazioni supportate
Ogni classe Artifact
può implementare una qualsiasi delle seguenti interfacce per indicare
le operazioni che supporta:
Transformable
: consente di utilizzareArtifact
come input per unTask
che trasformazioni arbitrarie e restituisce una nuova versioneArtifact
.Appendable
: si applica solo agli artefatti che sono sottoclassi diArtifact.Multiple
. Significa che è possibile aggiungereArtifact
, ovveroTask
personalizzato può creare nuove istanze di questo tipoArtifact
che verranno aggiunte all'elenco esistente.Replaceable
: si applica solo agli artefatti che sono sottoclassi diArtifact.Single
. Un elementoArtifact
sostituibile può essere sostituito da un valore completamente nuovo generato come output di unTask
.
Oltre alle tre operazioni di modifica degli artefatti, ogni artefatto supporta
Un get()
(o getAll()
)
che restituisce un Provider
con la versione finale dell'artefatto
(al termine di tutte le operazioni).
Più plug-in possono aggiungere un numero qualsiasi di operazioni sugli artefatti nella pipeline
dal callback onVariants()
e AGP garantirà che siano concatenati correttamente
che tutte le attività vengano eseguite al momento giusto e che gli artefatti vengano prodotti correttamente
aggiornato. Ciò significa che quando un'operazione modifica qualsiasi output aggiungendo
per sostituirli o trasformarli, la prossima operazione vedrà la versione aggiornata
di questi artefatti come input e così via.
Il punto di accesso per la registrazione delle operazioni è la classe Artifacts
.
Il seguente snippet di codice mostra come ottenere l'accesso a un'istanza di
Artifacts
da una proprietà sull'oggetto Variant
in onVariants()
di Google.
Puoi quindi trasmettere il tuo TaskProvider
personalizzato per ottenere un
TaskBasedOperation
(1) e utilizzarlo per collegare i suoi ingressi e le sue uscite utilizzando uno dei
wiredWith*
metodi (2).
Il metodo esatto da scegliere dipende dalla cardinalità e
Tipo FileSystemLocation
implementato dal Artifact
che vuoi trasformare.
Infine, passi il tipo Artifact
a un metodo che rappresenta il
un'operazione sull'oggetto *OperationRequest
che ottieni 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
e un
RegularFile
. Per questo motivo dobbiamo usare il metodo wiredWithFiles
, che
accetta un singolo riferimento RegularFileProperty
per l'input e un singolo
RegularFileProperty
per l'output. Esistono altri metodi wiredWith*
su
la classe TaskBasedOperation
che funzionerà per altre combinazioni di Artifact
cardinalità e tipi di FileSystemLocation
.
Per saperne di più sull'estensione di AGP, ti consigliamo di leggere le seguenti sezioni dal manuale del sistema di compilazione Gradle:
- Sviluppo di plug-in Gradle personalizzati
- Implementazione dei plug-in Gradle
- Sviluppo di tipi di attività Gradle personalizzati
- Configurazione lazy
- Evitare la configurazione delle attività