In genere, le applicazioni Android vengono create utilizzando il sistema di compilazione Gradle. Prima di esaminare i dettagli su come configurare la compilazione, esploreremo i concetti alla base della compilazione in modo da poter vedere il sistema nel suo complesso.
Che cos'è una build?
Un sistema di compilazione trasforma il codice sorgente in un'applicazione eseguibile. Le build spesso richiedono più strumenti per analizzare, compilare, collegare e pacchettizzare l'applicazione o la libreria. Gradle utilizza un approccio basato su attività per organizzare ed eseguire questi comandi.
Le attività incapsulano i comandi che traducono gli input in output. I plug-in definiscono le attività e la relativa configurazione. L'applicazione di un plug-in alla compilazione ne registra le attività e le collega utilizzando gli input e gli output. Ad esempio, l'applicazione del plug-in Android per Gradle (AGP) al file di compilazione registrerà tutte le attività necessarie per compilare un APK o una libreria Android. Il plug-in java-library
ti consente di creare un file JAR dal codice sorgente Java. Esistono plug-in simili per Kotlin e altri linguaggi, ma altri plug-in
sono pensati per estenderli. Ad esempio, il plug-in protobuf
è progettato per aggiungere il supporto di protobuf ai plug-in esistenti come AGP o java-library
.
Gradle preferisce le convenzioni alla configurazione, pertanto i plug-in avranno valori predefiniti utili, ma puoi configurare ulteriormente la compilazione tramite un linguaggio specifico per il dominio (DSL) dichiarativo. Il DSL è progettato in modo da poter specificare cosa costruire, anziché come. La logica dei plug-in gestisce il "come". Questa configurazione è specificata in diversi file di compilazione nel progetto (e nei sottoprogetti).
Gli input delle attività possono essere file e directory, nonché altre informazioni codificate come tipi Java (interi, stringhe o classi personalizzate). Gli output possono essere solo directory o file, in quanto devono essere scritti sul disco. Collegando l'output di un'attività all'input di un'altra, le attività vengono collegate in modo che una debba essere eseguita prima dell'altra.
Sebbene Gradle supporti la scrittura di codice e dichiarazioni di attività arbitrari nei file di compilazione, questo può rendere più difficile per gli strumenti comprendere la compilazione e per te gestirla. Ad esempio, puoi scrivere test per il codice all'interno dei plug-in, ma non nei file di compilazione. Devi invece limitare le dichiarazioni della logica di compilazione e delle attività ai plug-in (definiti da te o da altri) e dichiarare come vuoi utilizzare questa logica nei file di compilazione.
Cosa succede quando viene eseguita una compilazione Gradle?
Le build di Gradle vengono eseguite in tre fasi. Ciascuna di queste fasi esegue parti diverse di codice che definisci nei file di build.
- L'inizializzazione determina quali progetti e sottoprogetti sono inclusi nella compilazione e configura i percorsi di classe contenenti i file di compilazione e i plug-in applicati. Questa fase si concentra su un file di impostazioni in cui dichiari i progetti da compilare e le posizioni da cui recuperare plug-in e librerie.
- Configurazione registra le attività per ogni progetto ed esegue il file di compilazione per applicare la specifica di compilazione dell'utente. È importante capire che il codice di configurazione non avrà accesso ai dati o ai file prodotti durante l'esecuzione.
- Esecuzione esegue la "costruzione" effettiva dell'applicazione. L'output della configurazione è un grafo aciclico diretto (DAG) di attività che rappresenta tutti i passaggi di compilazione richiesti dall'utente (le attività fornite sulla riga di comando o come predefinite nei file di compilazione). Questo grafico rappresenta la relazione tra le attività, esplicita nella dichiarazione di un'attività o basata sui suoi input e output. Se un'attività ha un input che è l'output di un'altra attività, deve essere eseguita dopo l'altra attività. Questa fase esegue le attività non aggiornate nell'ordine definito nel grafo. Se gli input di un'attività non sono cambiati dall'ultima esecuzione, Gradle la salta.
Per ulteriori informazioni, consulta il ciclo di vita del build di Gradle.
DSL di configurazione
Gradle utilizza un linguaggio specifico del dominio (DSL) per configurare le build. Questo approccio dichiarativo si concentra sulla specifica dei dati anziché sulla scrittura di istruzioni passo passo (imperative). Puoi scrivere i file di compilazione utilizzando Kotlin o Groovy, ma ti consigliamo vivamente di utilizzare Kotlin.
I DSL cercano di semplificare a tutti, esperti di dominio e programmatori, la possibilità di contribuire a un progetto, definendo un piccolo linguaggio che rappresenti i dati in modo più naturale. I plug-in Gradle possono estendere il DSL per configurare i dati di cui hanno bisogno per le loro attività.
Ad esempio, la configurazione della parte Android della build potrebbe avere il seguente aspetto:
Kotlin
android { namespace = "com.example.app" compileSdk = 34 // ... defaultConfig { applicationId = "com.example.app" minSdk = 34 // ... } }
Groovy
android { namespace 'com.example.myapplication' compileSdk 34 // ... defaultConfig { applicationId "com.example.myapplication" minSdk 24 // ... } }
Dietro le quinte, il codice DSL è simile a:
fun Project.android(configure: ApplicationExtension.() -> Unit) {
...
}
interface ApplicationExtension {
var compileSdk: Int
var namespace: String?
val defaultConfig: DefaultConfig
fun defaultConfig(configure: DefaultConfig.() -> Unit) {
...
}
}
Ogni blocco nel DSL è rappresentato da una funzione che accetta un lambda per configurarlo e una proprietà con lo stesso nome per accedervi. In questo modo, il codice nei file di compilazione sembra più una specifica dei dati.
Dipendenze esterne
Il sistema di compilazione Maven ha introdotto una specifica per le dipendenze, nonché un sistema di archiviazione e gestione. Le librerie vengono archiviate in repository (server o directory), con metadati che includono la loro versione e le dipendenze da altre librerie. Specifichi i repository da cercare, le versioni delle dipendenze che vuoi utilizzare e il sistema di compilazione le scarica durante la compilazione.
Gli elementi Maven vengono identificati dal nome del gruppo (azienda, sviluppatore e così via), dal nome dell'elemento (il nome della libreria) e dalla relativa versione. In genere viene rappresentato come group:artifact:version
.
Questo approccio migliora notevolmente la gestione delle build. Spesso questi repository vengono chiamati "repository Maven", ma il punto è il modo in cui gli elementi vengono pacchettizzati e pubblicati. Questi repository e metadati sono stati riutilizzati in diversi sistemi di compilazione, tra cui Gradle (che può pubblicare in questi repository). I repository pubblici consentono la condivisione per tutti e i repository aziendali mantengono le dipendenze interne in-house.
Puoi anche modularizzare il progetto in sottoprogetti (chiamati anche "moduli" in Android Studio), che possono essere utilizzati anche come dipendenze. Ogni sottoprogetto genera output (ad esempio jar) che possono essere utilizzati dai sottoprogetti o dal progetto di primo livello. In questo modo è possibile migliorare i tempi di compilazione isolando le parti che devono essere ricostruite, nonché separare meglio le responsabilità nell'applicazione.
Scopriremo di più su come specificare le dipendenze in Aggiungere le dipendenze di compilazione.
Creare varianti di compilazione
Quando crei un'applicazione per Android, in genere è consigliabile creare più varianti. Le varianti contengono codice diverso o sono create con opzioni diverse e sono composte da tipi di build e varianti di prodotto.
I tipi di build variano in base alle opzioni di build dichiarate. Per impostazione predefinita, AGP configura i tipi di build "release" e "debug", ma puoi modificarli e aggiungerne altri (ad esempio per lo staging o i test interni).
Una build di debug non riduce al minimo o offusca l'applicazione, velocizzando la sua compilazione e preservando tutti i simboli così come sono. Inoltre, contrassegna l'applicazione come "debuggabile", firmandola con una chiave di debug generica e consentendo l'accesso ai file dell'applicazione installati sul dispositivo. In questo modo, è possibile esplorare i dati salvati in file e database durante l'esecuzione dell'applicazione.
Una release build ottimizza l'applicazione, la firma con la chiave di release e protegge i file dell'applicazione installati.
Utilizzando i flavor del prodotto, puoi modificare il codice sorgente incluso e le varianti di dipendenza per l'applicazione. Ad esempio, potresti creare versioni "demo" e "complete" per la tua applicazione oppure versioni "senza costi" e "a pagamento". Scrivi l'origine comune in una directory del set di origini "principale" e superi o aggiungi l'origine in un set di origini denominato in base al flavor.
AGP crea varianti per ogni combinazione di tipo di build e versione del prodotto. Se
non definisci i gusti, le varianti vengono denominate in base ai tipi di build. Se ne definisci entrambi, la variante viene denominata <flavor><Buildtype>
. Ad esempio, con i tipi di compilazione release
e debug
e i gusti demo
e full
, AGP creerà le varianti:
demoRelease
demoDebug
fullRelease
fullDebug
Passaggi successivi
Ora che hai visto i concetti di compilazione, dai un'occhiata alla struttura di compilazione Android nel tuo progetto.