Panoramica della build Gradle

Le app per Android vengono in genere create utilizzando il sistema di compilazione Gradle. Prima di entrare nei dettagli della configurazione della 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 build 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 su attività per organizzare ed eseguire questi comandi.

Le attività incapsulano i comandi che traducono gli input in output. I plugin definiscono le attività e la relativa configurazione. L'applicazione di un plug-in alla build registra le sue attività e le collega tra loro 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 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 è pensato 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 vengono forniti con buoni valori predefiniti, ma puoi configurare ulteriormente la build tramite un linguaggio specifico di dominio (DSL) dichiarativo. Il DSL è progettato in modo da consentirti di specificare cosa creare, anziché come crearlo. La logica dei 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. Collegando l'output di un'attività all'input di un'altra attività, le attività vengono collegate 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, questo può rendere più difficile per gli strumenti comprendere la build e per te gestirla. 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 build e le dichiarazioni delle attività ai plug-in (definiti da te o da qualcun altro) 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 diverse del codice che definisci nei file di build.

  • L'inizializzazione determina quali progetti e sottoprogetti sono inclusi nella build e configura i percorsi di classe 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 posizioni 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 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 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 saperne di più, consulta il 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 DSL cercano 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 di cui hanno bisogno per le loro 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)
        }
        // ...
    }
}

Alla moda

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 assomiglia di più a una specifica dei dati.

Dipendenze esterne

Il sistema di build Maven ha introdotto un sistema di specifica, archiviazione e gestione delle dipendenze. Le librerie sono archiviate in repository (server o directory), con metadati che includono la versione e le dipendenze da altre librerie. Specifica i repository in cui eseguire la ricerca e le versioni delle dipendenze che vuoi utilizzare. Il sistema di build li 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, questo valore è rappresentato come group:artifact:version.

Questo approccio migliora notevolmente la gestione delle build. Spesso questi repository vengono chiamati "repository Maven", ma tutto dipende dal 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 tutti, mentre i repository aziendali mantengono le dipendenze interne all'interno dell'azienda.

Puoi anche modularizzare il tuo progetto in progetti secondari (noti anche come "moduli" in Android Studio), che possono essere utilizzati anche come dipendenze. Ogni sottoprogetto produce output (come i file jar) che possono essere utilizzati dai sottoprogetti o dal progetto di primo livello. In questo modo, è possibile migliorare i tempi di build isolando le parti che devono essere ricreate, oltre a separare meglio le responsabilità nell'applicazione.

Per maggiori dettagli su come specificare le dipendenze, consulta Aggiungi dipendenze di build.

Varianti di compilazione

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 versioni 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 test interni o di gestione temporanea).

Una build di debug non minimizza né offusca l'applicazione, velocizzando la sua build e preservando tutti i simboli così come sono. Inoltre, contrassegna l'applicazione come "abilitata al debug", 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 build di release ottimizza l'applicazione, la firma con la chiave di release e protegge i file dell'applicazione installata.

Utilizzando le versioni prodotto, puoi modificare le varianti di origine e di dipendenza incluse per l'applicazione. Ad esempio, potresti voler creare versioni "demo" e "complete" della tua applicazione, oppure versioni "senza costi" e "a pagamento". Scrivi il codice sorgente comune in una directory "main" del set di risorse e esegui l'override o aggiungi il codice sorgente in un set di risorse denominato in base alla versione.

AGP crea varianti per ogni combinazione di tipo di build e versione 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 versioni 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.