Les applications Android sont généralement créées à l'aide du système de compilation Gradle. Avant d'examiner en détail la configuration de votre build, nous allons explorer les concepts qui le sous-tendent afin que vous puissiez voir le système dans son ensemble.
Qu'est-ce qu'une version ?
Un système de compilation transforme votre code source en application exécutable. Les compilations impliquent souvent plusieurs outils pour analyser, compiler, associer et empaqueter votre application ou bibliothèque. Gradle utilise une approche basée sur les tâches pour organiser et exécuter ces commandes.
Les tâches encapsulent des commandes qui traduisent leurs entrées en sorties. Les plug-ins définissent les tâches et leur configuration. L'application d'un plug-in à votre compilation enregistre ses tâches et les relie à l'aide de leurs entrées et sorties. Par exemple, l'application du plug-in Android Gradle (AGP) à votre fichier de compilation enregistre toutes les tâches nécessaires à la compilation d'un APK ou d'une bibliothèque Android. Le plug-in java-library vous permet de créer un fichier JAR à partir du code source Java. Des plug-ins similaires existent pour Kotlin et d'autres langages, mais d'autres plug-ins sont destinés à étendre les plug-ins. Par exemple, le plug-in protobuf est conçu pour ajouter la prise en charge de Protobuf aux plug-ins existants tels qu'AGP ou java-library.
Gradle préfère la convention à la configuration. Les plug-ins sont donc fournis avec de bonnes valeurs par défaut, mais vous pouvez configurer davantage la compilation à l'aide d'un langage spécifique au domaine (DSL) déclaratif. Le DSL est conçu pour vous permettre de spécifier ce qu'il faut créer, plutôt que comment le créer. La logique des plug-ins gère le "comment". Cette configuration est spécifiée dans plusieurs fichiers de compilation de votre projet (et de vos sous-projets).
Les entrées de tâches peuvent être des fichiers et des répertoires, ainsi que d'autres informations encodées en tant que types Java (entiers, chaînes ou classes personnalisées). Les sorties ne peuvent être que des répertoires ou des fichiers, car elles doivent être écrites sur le disque. En connectant la sortie d'une tâche à l'entrée d'une autre, vous liez les tâches entre elles de sorte que l'une doit être exécutée avant l'autre.
Bien que Gradle permette d'écrire du code arbitraire et des déclarations de tâches dans vos fichiers de compilation, cela peut rendre plus difficile la compréhension de votre compilation par les outils et la maintenance par vous. Par exemple, vous pouvez écrire des tests pour le code à l'intérieur des plug-ins, mais pas dans les fichiers de compilation. Au lieu de cela, vous devez limiter la logique de compilation et les déclarations de tâches aux plug-ins (que vous ou quelqu'un d'autre définissez) et déclarer la façon dont vous souhaitez utiliser cette logique dans vos fichiers de compilation.
Que se passe-t-il lorsqu'une compilation Gradle s'exécute ?
Les compilations Gradle s'exécutent en trois phases. Chacune de ces phases exécute différentes parties du code que vous définissez dans vos fichiers de compilation.
- L'initialisation détermine les projets et sous-projets inclus dans la compilation, et configure les chemins de classe contenant vos fichiers de compilation et les plug-ins appliqués. Cette phase se concentre sur un fichier de paramètres dans lequel vous déclarez les projets à compiler et les emplacements à partir desquels extraire les plug-ins et les bibliothèques.
- La configuration enregistre les tâches pour chaque projet et exécute le fichier de compilation pour appliquer les spécifications de compilation de l'utilisateur. Il est important de comprendre que votre code de configuration n'aura pas accès aux données ni aux fichiers produits lors de l'exécution.
- Execution effectue la compilation proprement dite de votre application. La configuration génère un graphe orienté acyclique (DAG) de tâches, qui représente toutes les étapes de compilation requises demandées par l'utilisateur (les tâches fournies sur la ligne de commande ou en tant que valeurs par défaut dans les fichiers de compilation). Ce graphique représente la relation entre les tâches, qu'elle soit explicite dans la déclaration d'une tâche ou basée sur ses entrées et sorties. Si une tâche a une entrée qui est la sortie d'une autre tâche, elle doit s'exécuter après l'autre tâche. Cette phase exécute les tâches obsolètes dans l'ordre défini dans le graphique. Si les entrées d'une tâche n'ont pas changé depuis sa dernière exécution, Gradle l'ignore.
Pour en savoir plus, consultez la section Cycle de vie de la compilation de Gradle.
DSL de configuration
Gradle utilise un langage spécifique au domaine (DSL) pour configurer les compilations. Cette approche déclarative consiste à spécifier vos données plutôt qu'à écrire des instructions impératives détaillées. Vous pouvez écrire vos fichiers de compilation en Kotlin ou en Groovy, mais nous vous recommandons vivement d'utiliser Kotlin.
Les DSL visent à permettre à tous, experts du domaine et programmeurs, de contribuer à un projet en définissant un petit langage qui représente les données de manière plus naturelle. Les plug-ins Gradle peuvent étendre le DSL pour configurer les données dont ils ont besoin pour leurs tâches.
Par exemple, la configuration de la partie Android de votre build peut ressembler à ceci :
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) } // ... } }
En arrière-plan, le code DSL ressemble à ceci :
fun Project.android(configure: ApplicationExtension.() -> Unit) {
...
}
interface ApplicationExtension {
var namespace: String?
fun compileSdk(configure: CompileSdkSpec.() -> Unit) {
...
}
val defaultConfig: DefaultConfig
fun defaultConfig(configure: DefaultConfig.() -> Unit) {
...
}
}
Chaque bloc du DSL est représenté par une fonction qui prend un lambda pour le configurer et une propriété du même nom pour y accéder. Le code de vos fichiers de compilation ressemble ainsi davantage à une spécification de données.
Dépendances externes
Le système de compilation Maven a introduit un système de spécification, de stockage et de gestion des dépendances. Les bibliothèques sont stockées dans des dépôts (serveurs ou répertoires), avec des métadonnées incluant leur version et leurs dépendances vis-à-vis d'autres bibliothèques. Vous spécifiez les dépôts à rechercher, les versions des dépendances que vous souhaitez utiliser et le système de compilation les télécharge lors de la compilation.
Les artefacts Maven sont identifiés par le nom du groupe (entreprise, développeur, etc.), le nom de l'artefact (nom de la bibliothèque) et la version de cet artefact. Elle est généralement représentée par group:artifact:version.
Cette approche améliore considérablement la gestion des compilations. Vous entendrez souvent parler de "dépôts Maven" pour désigner ces dépôts, mais il s'agit en fait de la façon dont les artefacts sont empaquetés et publiés. Ces dépôts et métadonnées ont été réutilisés dans plusieurs systèmes de compilation, y compris Gradle (et Gradle peut publier dans ces dépôts). Les dépôts publics permettent le partage pour tous, et les dépôts d'entreprise conservent les dépendances internes en interne.
Vous pouvez également modulariser votre projet en sous-projets (également appelés "modules" dans Android Studio), qui peuvent également être utilisés comme dépendances. Chaque sous-projet produit des sorties (telles que des fichiers JAR) qui peuvent être utilisées par des sous-projets ou votre projet de premier niveau. Cela peut améliorer le temps de compilation en isolant les parties à recompiler, ainsi qu'en séparant mieux les responsabilités dans l'application.
Nous verrons plus en détail comment spécifier les dépendances dans Ajouter des dépendances de compilation.
Variantes de compilation
Lorsque vous créez une application Android, vous souhaitez généralement créer plusieurs variants. Les variantes contiennent du code différent ou sont créées avec des options différentes. Elles sont composées de types de compilation et de types de produit.
Les types de compilation varient selon les options de compilation déclarées. Par défaut, AGP configure les types de compilation "release" et "debug", mais vous pouvez les ajuster et en ajouter d'autres (par exemple, pour les tests internes ou de préproduction).
Une version de débogage ne réduit ni n'obfusque votre application, ce qui accélère sa compilation et préserve tous les symboles tels quels. Elle marque également l'application comme "débogable", la signe avec une clé de débogage générique et permet d'accéder aux fichiers de l'application installée sur l'appareil. Cela permet d'explorer les données enregistrées dans des fichiers et des bases de données lors de l'exécution de l'application.
Une version Release optimise l'application, la signe avec votre clé de release et protège les fichiers d'application installés.
Les types de produit vous permettent de modifier les variantes de source et de dépendance incluses pour l'application. Par exemple, vous pouvez créer des types "demo" et "full" pour votre application, ou encore des types "free" et "paid". Vous écrivez votre source commune dans un répertoire d'ensemble de sources "main", puis remplacez ou ajoutez une source dans un ensemble de sources portant le nom du type.
AGP crée des variantes pour chaque combinaison de type de compilation et de type de produit. Si vous ne définissez pas de types de produit, les variantes sont nommées d'après les types de compilation. Si vous définissez les deux, la variante est nommée <flavor><Buildtype>. Par exemple, avec les types de compilation release et debug, et les saveurs demo et full, AGP créera les variantes suivantes :
demoReleasedemoDebugfullReleasefullDebug
Étapes suivantes
Maintenant que vous avez vu les concepts de compilation, examinez la structure de compilation Android dans votre projet.