Composition est le processus central de Jetpack Compose, au cours duquel un arbre UI vivant affiché à l'écran est construit à partir de fonctions Composable descriptives. Contrairement au système View d'Android, où les mises en page étaient chargées depuis XML et converties en objets immuables, la Composition fonctionne comme un système dynamique : les fonctions s'exécutent, créent des slots en mémoire, forment une hiérarchie de nœuds et la lient à l'état. Selon Google Android Developers, 2026, comprendre la Composition est essentiel pour optimiser les performances des applications Compose.
Points clés
Composition est le processus d'exécution des fonctions Composable, aboutissant à une représentation interne de l'interface utilisateur sous forme d'arbre de nœuds. Chaque nœud de cet arbre correspond soit à un composant intégré (Text, Button, Image), soit à un appel de fonction Composable définie par l'utilisateur. La Composition ne crée pas directement d'objets View Android — elle construit une description abstraite qui est ensuite traitée par les phases Layout et Drawing.
La caractéristique clé de la Composition est sa redémarrabilité. Chaque fonction Composable au sein de la composition peut être redémarrée à tout moment si ses paramètres d'entrée ou les objets d'état qu'elle lit ont changé. Le système ne redémarre pas tout l'arbre — seulement les fonctions qui dépendent réellement des données modifiées.
Techniquement, la Composition est gérée via Composer — un moteur interne que le compilateur Kotlin intègre dans chaque fonction Composable. Composer écrit dans des slots (groupes de positions) des informations sur les fonctions appelées, avec quels paramètres et dans quel ordre. Lors des appels suivants, Composer compare les nouvelles données avec les données stockées et décide de redémarrer ou non.
Le processus de construction de l'arbre UI commence par l'appel de la méthode setContent dans une Activity ou un Fragment. Cette méthode crée la Composition initiale et commence à exécuter la fonction Composable racine. Ensuite, chaque fonction Composable imbriquée ajoute ses nœuds à l'arbre, formant une hiérarchie : Row contient Text et Button, Column contient Image et Card, et ainsi de suite.
Chaque nœud de l'arbre reçoit une clé de position unique, basée sur sa position dans le code source. Cette clé est utilisée pour identifier le nœud lors des exécutions ultérieures. La clé de position est la raison pour laquelle l'ordre d'appel des fonctions Composable ne doit pas dépendre de conditions : si dans une exécution A -> B est appelé, et dans la suivante B -> A, Compose ne pourra pas faire correspondre les nœuds anciens et nouveaux.
@Composable
fun AppScreen() {
Column { // Nœud Column (position 1)
HeaderSection() // Nœud HeaderSection (position 2)
ContentSection() // Nœud ContentSection (position 3)
FooterSection() // Nœud FooterSection (position 4)
}
}
@Composable
fun HeaderSection() {
Row { // Nœud Row (position 2.1)
Text("Titre") // Nœud Text (position 2.2)
Icon(...) // Nœud Icon (position 2.3)
}
}
Dans cet exemple, chaque appel reçoit une position basée sur l'ordre dans le code. Column (position 1) contient trois nœuds enfants (positions 2, 3, 4). HeaderSection ajoute deux autres nœuds enfants (2.1, 2.2, 2.3). Si dans la prochaine recomposition ContentSection est appelé avant HeaderSection, Composer ne pourra pas faire correspondre correctement les nœuds — d'où la règle : l'ordre des appels de fonctions Composable doit être stable.
L'état dans Composition est géré via des objets de type State<T>. Lorsqu'une fonction Composable lit une valeur d'un State via une propriété déléguée (by), elle enregistre une dépendance à ce State. Lorsque la valeur change, toutes les fonctions qui ont lu ce State sont marquées pour redémarrage dans la phase de composition suivante.
Le mécanisme d'enregistrement des dépendances s'appelle le système de snapshot. Chaque fois que State change, un snapshot enregistre toutes les modifications et notifie à Composer quelles fonctions dépendent de ce State. Il est important de comprendre : la lecture de State dans du code non-Composable (par exemple, dans une lambda onClick) n'enregistre pas de dépendance — seule la lecture dans une fonction Composable ou dans des lambdas exécutées dans le contexte de composition.
Le système de snapshot fonctionne de manière transactionnelle : plusieurs changements de State au sein d'un même événement sont combinés en une seule transaction, évitant de multiples recompositions. Ceci est particulièrement important lors du traitement des gestes : un mouvement change plusieurs objets State, mais Compose n'effectue qu'une seule recomposition.
@Composable
fun StateExample() {
var text by remember { mutableStateOf("Hello") }
var isVisible by remember { mutableStateOf(true) }
Column {
Text(text) // enregistre la dépendance à text
if (isVisible) { // enregistre la dépendance à isVisible
TextField(value = text, onValueChange = { text = it })
}
Button(onClick = { isVisible = !isVisible }) {
Text(if (isVisible) "Masquer" else "Afficher")
}
}
}
Changer text déclenche une recomposition uniquement de Column, Text et TextField. Column, Button et la condition isVisible restent inchangés. Cet isolement de la recomposition est un avantage clé de Compose par rapport aux systèmes qui redessinent l'écran entier. Chaque fonction Composable ne suit que les objets State qu'elle lit directement.
Composition et Recomposition sont deux modes différents d'exécution des fonctions Composable. La Composition a lieu une fois lors de la création de l'écran : le système exécute toutes les fonctions Composable avec des valeurs initiales et construit l'arbre UI initial. La Recomposition a lieu plusieurs fois lorsque les données changent : le système ne redémarre que les fonctions qui dépendent de l'état modifié.
Mode Composition active tous les nœuds de l'arbre, alloue des slots pour chaque fonction et enregistre tous les descendants. La Recomposition fonctionne sélectivement : Compose compare les nouvelles et anciennes valeurs des paramètres de chaque fonction, et si elles n'ont pas changé — la fonction n'est pas exécutée (skipping).
La Composition et la Recomposition diffèrent en coût. La première Composition est plus coûteuse car elle nécessite la construction complète de l'arbre et l'allocation des slots. La Recomposition est moins coûteuse, surtout si la plupart des fonctions sont stables — leurs paramètres sont comparés par equals, et Compose ignore leur invocation. Pour des performances maximales, il faut viser à ce que la plupart des recompositions affectent le moins de fonctions possible.
| Caractéristique | Composition | Recomposition |
|---|---|---|
| Quand cela se produit | Une fois, au premier affichage | Plusieurs fois, lors du changement de données |
| Portée | Arbre entier | Fonctions modifiées uniquement |
| Comparaison des paramètres | Non effectuée | Effectuée pour le skipping |
| Création de slots | Oui, tous les slots créés | Nouveaux nœuds uniquement |
CompositionLocal est un mécanisme de transmission implicite de données à travers l'arbre de composition. Il résout le problème lorsqu'un paramètre doit être transmis à travers des dizaines de fonctions Composable imbriquées qui ne l'utilisent pas directement. Au lieu d'une chaîne explicite de paramètres, les données sont définies au niveau supérieur et lues dans toute fonction imbriquée via CompositionLocal.current.
MaterialTheme est l'exemple le plus connu de CompositionLocal. Tous les composants Compose lisent les couleurs, la typographie et les formes via MaterialTheme.colorScheme, MaterialTheme.typography, MaterialTheme.shapes, sans les recevoir via des paramètres. Les développeurs peuvent créer leurs propres CompositionLocal pour des données telles que l'utilisateur actuel, les paramètres de localisation ou la configuration de l'écran.
Une limitation importante : CompositionLocal ne doit pas être utilisé pour des données changeant fréquemment (position de défilement, texte dans un champ de saisie). Un composant qui lit CompositionLocal redémarre à chaque changement de valeur, donc pour les données dynamiques, il est préférable d'utiliser des paramètres explicites ou State. CompositionLocal est optimal pour les données de configuration qui changent rarement ou jamais.
val LocalUser = compositionLocalOf<User?> { null }
@Composable
fun AppRoot(user: User, content: @Composable () -> Unit) {
CompositionLocalProvider(LocalUser.provides(user)) {
content()
}
}
@Composable
fun UserAvatar() {
val user = LocalUser.current // lecture sans paramètre explicite
AsyncImage(model = user?.avatarUrl, contentDescription = "Avatar")
}
CompositionLocalProvider crée une portée dans laquelle LocalUser.current retourne la valeur spécifiée. UserAvatar lit l'utilisateur sans passer explicitement le paramètre via des fonctions intermédiaires. Ceci est particulièrement précieux dans les hiérarchies profondes où les données ne sont nécessaires que dans quelques nœuds feuilles.
Foire aux questions
Modifier State pendant la Composition planifie une nouvelle recomposition, qui s'exécutera après la fin de la recomposition en cours. Aucune boucle infinie ne se produit : Compose garantit que chaque recomposition est effectuée dans une transaction séparée du système de snapshot.
Sur les appareils modernes, la Composition d'un écran avec 50–100 fonctions Composable prend 1–5 ms. Google recommande de rester dans les 16 ms pour une image à 60fps. Si la Composition dépasse cette limite, utilisez LazyColumn ou divisez l'écran en fonctions plus petites.
Le lancement manuel direct de la Composition n'est pas possible — il est géré par Composer automatiquement. Cependant, vous pouvez forcer une recomposition en modifiant State ou en appelant invalidate() sur le composable racine si vous avez accès à CompositionContext.
La hiérarchie View est un arbre immuable d'objets Java créé une fois. La Composition est un arbre virtuel qui est reconstruit à chaque changement de données. View stocke son état dans des variables d'instance, la Composition — dans des slots liés à la position d'appel de la fonction.
Si une fonction Composable n'est plus appelée (par exemple, une condition if devient false), Composition supprime son nœud et déclenche le nettoyage DisposableEffect. Lorsqu'elle réapparaît (if redevient true), un nouveau nœud est créé — l'ancien n'est pas restauré.
Résumé
Nous développerons une application mobile clé en main
IT Sectr crée des applications iOS et Android pour les startups et les entreprises depuis 2017. Nous vous conseillerons et vous proposerons la meilleure solution.
Lisez aussi