Panoramica della build Gradle

Le applicazioni Android vengono in genere create utilizzando il sistema di build Gradle. Prima di approfondire i dettagli su come configurare la build, esploreremo i concetti alla base della build in modo che tu possa 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 coinvolgono più strumenti per analizzare, compilare, collegare e creare pacchetti dell'applicazione o della libreria. Gradle utilizza un approccio basato sulle attività per organizzare ed eseguire questi comandi.

Le attività incapsulano i comandi che traducono i loro input in output. I plug-in definiscono le attività e la relativa configurazione. L'applicazione di un plug-in alla build registra le relative attività e le collega utilizzando i relativi input e output. Ad esempio, l'applicazione del plug-in Android per Gradle (AGP) al file di build registrerà tutte le attività necessarie per creare un APK o una libreria Android. Il plug-in java-library consente di creare un file JAR dal codice sorgente Java. Esistono plug-in simili per Kotlin e altri linguaggi, ma altri plug-in sono progettati per estendere i plug-in. Ad esempio, il plug-in protobuf è progettato per aggiungere il supporto di protobuf ai plug-in esistenti come AGP o java-library.

Gradle preferisce la convenzione alla configurazione, quindi i plug-in avranno buoni valori predefiniti, ma puoi configurare ulteriormente la build tramite un linguaggio DSL (Domain-Specific Language) dichiarativo. Il DSL è progettato in modo da poter specificare cosa creare, anziché come crearlo. La logica nei plug-in gestisce il "come". Questa configurazione è specificata in diversi file di build nel progetto (e nei sottoprogetti).

Gli input delle attività possono essere file e directory, nonché altre informazioni codificate come tipi Java (numeri interi, stringhe o classi personalizzate). Gli output possono essere solo directory o file, in quanto devono essere scritti su disco. Il collegamento dell'output di un'attività all'input di un'altra attività collega le attività in modo che una debba essere eseguita prima dell'altra.

Sebbene Gradle supporti la scrittura di codice arbitrario e dichiarazioni di attività nei file di build, ciò può rendere più difficile per gli strumenti comprendere la build e per te mantenerla. Ad esempio, puoi scrivere test per il codice all'interno dei plug-in, ma non nei file di build. Invece, devi limitare la logica di build e le dichiarazioni di attività ai plug-in (definiti da te o da altri) e dichiarare come vuoi utilizzare questa logica nei file di build.

Cosa succede quando viene eseguita una build Gradle?

Le build Gradle vengono eseguite in tre fasi. Ognuna di queste fasi esegue parti di codice diverse definite nei file di build.

  • La fase di inizializzazione determina quali progetti e sottoprogetti sono inclusi nella build e configura i classpath contenenti i file di build e i plug-in applicati. Questa fase si concentra su un file di impostazioni in cui dichiari i progetti da creare e le località da cui recuperare plug-in e librerie.
  • La fase di configurazione registra le attività per ogni progetto ed esegue il file di build per applicare la specifica di build dell'utente. È importante capire che il codice di configurazione non avrà accesso ai dati o ai file prodotti durante l'esecuzione.
  • La fase di esecuzione esegue la "creazione" effettiva dell'applicazione. L'output della configurazione è un grafo diretto aciclico (DAG) di attività, che rappresenta tutti i passaggi di build richiesti dall'utente (le attività fornite nella riga di comando o come valori predefiniti nei file di build). Questo grafico rappresenta la relazione tra le attività, esplicita nella dichiarazione di un'attività o basata sui relativi 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 grafico; se gli input di un'attività non sono cambiati dall'ultima esecuzione, Gradle la salterà.

Per ulteriori informazioni, consulta Ciclo di vita della build Gradle.

DSL di configurazione

Gradle utilizza un linguaggio DSL (Domain-Specific Language) 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 build utilizzando Kotlin o Groovy, ma ti consigliamo vivamente di utilizzare Kotlin.

I DSL tentano di semplificare il contributo a un progetto per tutti, esperti di dominio e programmatori, definendo un piccolo linguaggio che rappresenta i dati in modo più naturale. I plug-in Gradle possono estendere il DSL per configurare i dati necessari per le relative attività.

Ad esempio, la configurazione della parte Android della build potrebbe essere simile alla seguente:

Kotlin

android {
    namespace = "com.example.app"
    compileSdk {
        version = release(36) {
            minorApiLevel = 1
        }
    }
    // ...

    defaultConfig {
        applicationId = "com.example.app"
        minSdk {
            version = release(23)
        }
        targetSdk {
            version = release(36)
        }
        // ...
    }
}

Groovy

android {
    namespace = 'com.example.app'
    compileSdk {
        version = release(36) {
            minorApiLevel = 1
        }
    }
    // ...

    defaultConfig {
        applicationId = 'com.example.app'
        minSdk {
            version = release(23)
        }
        targetSdk {
            version = release(36)
        }
        // ...
    }
}

Dietro le quinte, il codice DSL è simile al seguente:

fun Project.android(configure: ApplicationExtension.() -> Unit) {
    ...
}

interface ApplicationExtension {
    var namespace: String?

    fun compileSdk(configure: CompileSdkSpec.() -> Unit) {
        ...
    }

    val defaultConfig: DefaultConfig

    fun defaultConfig(configure: DefaultConfig.() -> Unit) {
        ...
    }
}

Ogni blocco nel DSL è rappresentato da una funzione che accetta una lambda per configurarlo e da una proprietà con lo stesso nome per accedervi. In questo modo, il codice nei file di build sembra più una specifica di dati.

Dipendenze esterne

Il sistema di compilazione Maven ha introdotto un sistema di specifica, archiviazione e gestione delle dipendenze. Le librerie vengono archiviate in repository (server o directory) con metadati che includono la 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 build.

Gli artefatti Maven sono identificati dal nome del gruppo (azienda, sviluppatore e così via), dal nome dell'artefatto (il nome della libreria) e dalla versione dell'artefatto. In genere è rappresentato come group:artifact:version.

Questo approccio migliora notevolmente la gestione delle build. Spesso sentirai parlare di questi repository come "repository Maven", ma si tratta del modo in cui gli artefatti vengono creati e pubblicati. Questi repository e metadati sono stati riutilizzati in diversi sistemi di build, tra cui Gradle (e Gradle può pubblicare in questi repository). I repository pubblici consentono la condivisione per tutti, mentre i repository aziendali mantengono le dipendenze interne in sede.

Puoi anche modularizzare il progetto in sottoprogetti (noti anche come "moduli" in Android Studio), che possono essere utilizzati anche come dipendenze. Ogni sottoprogetto produce output (ad esempio file JAR) che possono essere utilizzati da sottoprogetti o dal progetto di primo livello. Ciò può migliorare il tempo di compilazione isolando le parti che devono essere ricompilate, nonché separando meglio le responsabilità nell'applicazione.

Esamineremo più in dettaglio come specificare le dipendenze in Aggiungere dipendenze di build.

Varianti di build

Quando crei un'app per Android, in genere vuoi creare più varianti. Le varianti contengono codice diverso o vengono create con opzioni diverse e sono composte da tipi di build e varianti di prodotto.

I tipi di build variano le 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 la gestione temporanea o i test interni).

Una build di debug non riduce o offusca l'applicazione, velocizzandone la build e mantenendo tutti i simboli così come sono. Inoltre, contrassegna l'applicazione come "debuggable", la firma con una chiave di debug generica e consente l'accesso ai file dell'applicazione installata sul dispositivo. In questo modo è possibile esplorare i dati salvati in file e database durante l'esecuzione dell'applicazione.

Una build di release ottimizza l'applicazione, la firma con la chiave di release e protegge i file dell'applicazione installata.

Utilizzando le varianti di prodotto, puoi modificare le varianti di origine e le varianti di dipendenza incluse per l'applicazione. Ad esempio, potresti voler creare varianti "demo" e "full" per l'applicazione o magari varianti "free" e "paid". Scrivi l'origine comune in una directory del set di risorse "main" ed esegui l'override o aggiungi l'origine in un set di risorse denominato in base alla versione.

AGP crea varianti per ogni combinazione di tipo di compilazione e versione prodotto. Se non definisci le varianti, le varianti vengono denominate in base ai tipi di build. Se definisci entrambi, la variante viene denominata <flavor><Buildtype>. Ad esempio, con i tipi di build release e debug e le varianti demo e full, AGP creerà le seguenti varianti:

  • demoRelease
  • demoDebug
  • fullRelease
  • fullDebug

Passaggi successivi

Ora che hai visto i concetti di build, dai un'occhiata alla struttura di build Android nel tuo progetto.