Panoramica della build Gradle

Le applicazioni Android vengono in genere create utilizzando il sistema di compilazione Gradle. Prima di esaminare i dettagli della configurazione della build, esploreremo i concetti alla base della build 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 coinvolgono più strumenti per analizzare, compilare, collegare e pacchettizzare l'applicazione o la 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 loro 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 registra tutte le attività necessarie per creare 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 estendere i plug-in. Ad esempio, il plug-in protobuf ha lo scopo di aggiungere il supporto di protobuf a plug-in esistenti come AGP o java-library.

Gradle preferisce la convenzione alla configurazione, quindi i plug-in vengono forniti con buoni valori predefiniti, ma puoi configurare ulteriormente la build tramite un linguaggio specifico per il dominio (DSL) 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 del tuo progetto (e dei 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 collega le attività in modo che una debba essere eseguita prima dell'altra.

Sebbene Gradle supporti la scrittura di codice e dichiarazioni di attività arbitrarie nei file di build, ciò può rendere più difficile la comprensione della build e la manutenzione. Ad esempio, puoi scrivere test per il codice all'interno dei plug-in, ma non nei file di build. Devi invece limitare la logica di compilazione e le dichiarazioni delle 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. 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 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 compilare e le posizioni da cui recuperare plug-in e librerie.
  • Configuration 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.
  • Execution esegue la "compilazione" effettiva dell'applicazione. L'output della configurazione è un grafo aciclico orientato (DAG) di attività, che rappresenta tutti i passaggi di build richiesti dall'utente (le attività fornite sulla 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 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 grafico; se gli input di un'attività non sono cambiati dall'ultima esecuzione, Gradle la ignorerà.

Per maggiori informazioni, consulta la sezione Ciclo di vita della 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 build utilizzando Kotlin o Groovy, ma ti consigliamo vivamente di utilizzare Kotlin.

I linguaggi specifici del dominio 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 loro attività.

Ad esempio, la configurazione della parte Android della build potrebbe essere simile a questa:

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 a:

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 una proprietà con lo stesso nome per accedervi. In questo modo, il codice nei file di build sembra più una specifica dei dati.

Dipendenze esterne

Il sistema di build Maven ha introdotto una specifica di dipendenza, un sistema di archiviazione e gestione. Le librerie sono 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 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, questo valore è rappresentato come group:artifact:version.

Questo approccio migliora notevolmente la gestione delle build. Spesso questi repository vengono chiamati "repository Maven", ma si tratta solo del modo in cui gli artefatti vengono pacchettizzati 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 l'utilizzo da parte di tutti, mentre i repository aziendali mantengono le dipendenze interne all'azienda.

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

Vedremo più in dettaglio come specificare le dipendenze in Aggiungi dipendenze di build.

Varianti di compilazione

Quando crei un'applicazione Android, in genere vuoi creare più varianti. Le varianti contengono codice diverso o sono create con opzioni diverse e sono composte da tipi di build e varianti del 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 comprime né offusca l'applicazione, velocizzando la sua compilazione e conservando 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 l'origine inclusa e le varianti delle dipendenze per l'applicazione. Ad esempio, potresti voler creare varianti "demo" e "completa" per la tua applicazione oppure varianti "senza costi" e "a pagamento". Scrivi la sorgente comune in una directory source set "principale" e esegui l'override o aggiungi la sorgente in un source set denominato in base alla variante.

AGP crea varianti per ogni combinazione di tipo di build e variante del prodotto. Se non definisci i flavor, le varianti prendono il nome dai 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 varianti:

  • demoRelease
  • demoDebug
  • fullRelease
  • fullDebug

Passaggi successivi

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