Recomposition — définition, reconstruction de l'UI lors d'un changement d'état

Auteur : IT Sectr Publié le : 2026-06-27 Temps de lecture : 7 min

Recomposition est un mécanisme de Jetpack Compose qui reconstruit automatiquement des parties de l'interface utilisateur lorsque les données changent, sans mise à jour manuelle des éléments View. Lorsqu'une variable d'état dont dépend une fonction Composable change de valeur, Compose redémarre uniquement cette fonction, laissant le reste de l'arbre UI intact. Selon Google Android Developers, 2026, une bonne compréhension de la Recomposition permet de réduire les redessinages inutiles de 40 à 60%.

Points Clés

  • Recomposition — redémarrage des fonctions Composable lorsque leurs données d'entrée ou l'État changent
  • Skipping — saut des fonctions dont les paramètres n'ont pas changé (comparaison par equals)
  • Stability détermine si Compose peut sauter une fonction — les types stables se comparent correctement
  • Smart Recomposition ne redémarre que l'ensemble minimal de fonctions, pas tout l'arbre
  • La recomposition ne garantit pas le redessinage — Layout et Drawing peuvent sauter leur phase

Qu'est-ce que la Recomposition dans Jetpack Compose

Recomposition est la réexécution de fonctions Composable ayant déjà participé à la Composition, avec de nouvelles valeurs de paramètres ou d'état. L'objectif principal de la recomposition est de synchroniser l'arbre UI avec les données actuelles sans reconstruire toute l'interface depuis zéro. Contrairement à la Composition, qui se produit une fois, la Recomposition peut être déclenchée des centaines de fois pendant la durée de vie d'un écran.

La Recomposition fonctionne sur le principe de smart invalidation : Compose suit les objets State que chaque fonction Composable lit et marque pour redémarrage uniquement celles dont les dépendances ont changé. Ceci est réalisé grâce à un système de snapshot qui enregistre toutes les opérations de lecture de State pendant l'exécution, et un Composer qui associe ces dépendances à des fonctions spécifiques.

Il est important de comprendre : la recomposition ne signifie pas un redessinage immédiat de l'écran. Compose fonctionne en trois phases : Composition (construction de la description UI), Layout (calcul des tailles et positions) et Drawing (rendu sur le canevas). Si après la recomposition les tailles et positions des éléments n'ont pas changé, la phase Layout peut être sautée. Si l'apparence visuelle n'a pas changé — Drawing est sauté. Cette architecture en trois phases garantit un coût minimal pour chaque mise à jour de l'interface.

Déclencheurs de recomposition : qu'est-ce qui provoque le redémarrage des fonctions

Il existe trois déclencheurs principaux de recomposition. Le premier est un changement d'un objet State lu dans le corps d'une fonction Composable. Lorsque mutableStateOf ou derivedStateOf change de valeur, toutes les fonctions ayant enregistré la lecture de cet State dans la composition précédente sont marquées pour redémarrage.

Le deuxième déclencheur est un changement de paramètre d'une fonction Composable lorsqu'elle est appelée depuis une fonction parent. Si la fonction parent transmet une nouvelle valeur (par exemple, le texte ou le nombre a changé), la fonction enfant sera redémarrée, même si elle ne lit pas d'State en interne. Compose compare les nouvelles et anciennes valeurs des paramètres via equals, et si elles sont égales — la fonction peut être sautée.

Le troisième déclencheur est un changement de CompositionLocal via CompositionLocalProvider. Toutes les fonctions lisant CompositionLocal via .current sont redémarrées lorsque le fournisseur change. Ce mécanisme est utilisé par MaterialTheme : le changement de thème (clair/sombre) provoque la recomposition de tous les composants lisant MaterialTheme.colorScheme.

kotlin
@Composable
fun RecompositionDemo() {
    var counter by remember { mutableStateOf(0) }
    var text by remember { mutableStateOf("Hello") }

    Column {
        Text("Counter: $counter")  // recomposition when counter changes
        Text("Message: $text")    // recomposition when text changes

        Button(onClick = { counter++ }) {
            Text("+1")
        }
        Button(onClick = { text = "World" }) {
            Text("Change Text")
        }
    }
}

Cliquer sur le bouton +1 change counter, provoquant la recomposition uniquement de la première ligne Text et de la Column elle-même. La deuxième ligne Text affichant text ne redémarre pas. Cet isolement est le résultat du système de snapshot : chaque fonction Composable ne connaît que les objets State qu'elle a lus.

Optimisation de la recomposition : techniques pratiques

L'optimisation de la recomposition commence par le choix des structures de données appropriées. Utilisez des collections immuables (listOf, mapOf) plutôt que mutables (mutableListOf). Compose compare les paramètres via equals, et si une collection a changé mais equals a retourné true — la fonction ne redémarrera pas. Pour les collections mutables, utilisez SnapshotStateList, qui implémente un suivi correct des changements au niveau des éléments.

La deuxième technique consiste à extraire les parties stables de l'UI dans des fonctions Composable séparées. Si une partie de l'écran ne dépend pas d'un état changeant fréquemment, extrayez-la dans une fonction séparée avec des paramètres. Lorsque la recomposition se produit, la fonction stable reçoit les mêmes paramètres, Compose les compare et saute l'exécution. C'est plus efficace que de redémarrer cette partie au sein d'une grande fonction où certains paramètres ont changé.

La troisième technique concerne les clés dans LazyColumn. Spécifiez toujours une clé pour les éléments dans LazyColumn, LazyGrid et autres conteneurs paresseux. La clé permet à Compose d'identifier les éléments lors des changements de liste : ajout, suppression ou réorganisation. Sans clé, Compose redémarre tous les éléments de la liste lors de tout changement, ce qui sur les grandes listes provoque une dégradation notable des performances.

kotlin
// Optimized structure: stable parts extracted separately
@Composable
fun OptimizedScreen(items: List<Item>) {
    Column {
        Header()                         // does not depend on items — no recomposition
        Spacer(modifier = Modifier.height(8.dp))
        LazyColumn {
            items(items, key = { it.id }) { item ->
                ItemRow(item = item)   // recomposition only for changed items
            }
        }
    }
}

@Composable
fun Header() {
    Text("Item list", style = MaterialTheme.typography.headlineMedium)
}

@Composable
fun ItemRow(item: Item) {
    Text(item.title)
}

Skipping et Stability dans Compose

Skipping est un mécanisme par lequel Compose saute l'exécution d'une fonction Composable si tous ses paramètres n'ont pas changé. Pour que le skipping fonctionne correctement, les types de paramètres doivent être stables. Le compilateur Kotlin marque comme stables : les types primitifs (Int, Float, Boolean), String, les fonctions lambda, et les classes dont tous les champs sont stables et val.

Stability est l'annotation @Stable ou @Immutable qui peut être ajoutée aux classes de données personnalisées. Si une classe contient un champ mutable (var), le compilateur la considère instable, et Compose ne pourra pas sauter les fonctions avec de tels paramètres. Pour les classes avec var, utilisez @Stable si vous garantissez que la notification de changement sera envoyée via le système de snapshot.

Vous pouvez vérifier la stabilité avec le drapeau du compilateur -P "plugin:androidx.compose.compiler.plugins.kotlin:reportsDestination=. /reports". Il génère un rapport avec une liste de toutes les fonctions Composable et de leurs paramètres indiquant la stabilité. Si un paramètre est instable — le skipping est impossible pour cette fonction, et elle redémarrera à chaque recomposition du parent.

TypeStabilitéSkipping
Int, Float, BooleanStableOui
StringStableOui
LambdaStableOui
data class avec champs valStableOui
data class avec champs varInstableNon
List<String>InstableNon

Remarque : List<String> est considérée instable car c'est une interface, pas une implémentation concrète. Utilisez immutableListOf() de la bibliothèque Kotlin Collections Immutable ou encapsulez la liste dans une classe @Stable. Lambda est toujours stable car son equals ne compare que les références, et lorsqu'une nouvelle lambda est créée au site d'appel, la fonction parent redémarre également.

Surveillance de la recomposition dans Android Studio

Pour surveiller la recomposition, Android Studio fournit l'Inspecteur de disposition avec le mode Compose Recomposition Counts. Dans ce mode, chaque fonction Composable affiche le nombre de recompositions et les raisons du redémarrage. Cela permet de trouver rapidement les fonctions qui se recomposent trop souvent et de déterminer la cause racine — paramètres instables ou dépendances d'State inutiles.

Outils supplémentaires : Compose Metrics (collecte de statistiques via des tests d'instrumentation) et Recomposition Timer (mesure du temps d'exécution de chaque fonction). Google recommande d'activer ces outils pendant le profilage et de les désactiver dans les versions de production, car ils ajoutent jusqu'à 20% de surcharge par recomposition.

Lors de l'analyse des recompositions, recherchez les motifs de recomposition inutile : une fonction redémarre alors que son UI de sortie ne devrait pas changer. Une cause fréquente est l'utilisation de lambdas sans remember, où un nouvel objet lambda est créé à chaque fois et Compose considère le paramètre comme modifié. Solution : encapsulez les lambdas dans remember { } avec des captures fixes.

kotlin
// Bad: new lambda on every parent recomposition
@Composable
fun Parent() {
    Child(onClick = { doSomething() })  // new lambda every time
}

// Good: remember stabilizes the lambda
@Composable
fun Parent() {
    val onClick = remember { { doSomething() } }
    Child(onClick = onClick)  // same reference
}

Foire Aux Questions

La recomposition signifie-t-elle un redessinage de l'écran ?

Non, la recomposition n'est que la phase de Composition. Ensuite, Layout et Drawing s'exécutent. Si après la recomposition les tailles et positions des éléments n'ont pas changé, Layout et Drawing peuvent être complètement sautés, économisant les ressources GPU.

À quelle fréquence la recomposition peut-elle se produire ?

Lors des animations, la recomposition peut s'exécuter jusqu'à 120 fois par seconde (120fps). Pour une interaction normale — 10 à 60 fois par seconde. Il est important que chaque recomposition tienne dans le budget de trame (8–16 ms), sinon l'application ralentira.

Pourquoi une fonction se recompose-t-elle même si l'État n'a pas changé ?

La raison est un changement de paramètre de la fonction parent. Le parent redémarre (pour sa propre raison) et transmet une nouvelle valeur. Pour éviter cela, vérifiez la stabilité des paramètres et utilisez remember pour stabiliser les lambdas et les valeurs calculées.

Peut-on désactiver la recomposition pour une fonction spécifique ?

Il n'y a pas de désactivation directe, mais il existe un skipping forcé via readInComposition — l'État est lu en dehors du corps de la fonction, ce qui n'enregistre pas de dépendance. Utilisez-le avec précaution : la fonction ne réagira pas aux changements, ce qui peut conduire à une UI obsolète.

Qu'est-ce qui est le plus coûteux : Composition ou Recomposition ?

Composition est plus coûteuse car elle crée tous les slots et nœuds de l'arbre depuis zéro. Recomposition réutilise les slots existants et met seulement à jour leurs valeurs. En pratique, la Composition d'un écran prend 2–10 ms, tandis que la recomposition d'un seul élément prend 0.1–1 ms.

Résumé

  • Recomposition — redémarrage sélectif des fonctions Composable lorsque leur État ou leurs paramètres changent
  • Système de snapshot suit les dépendances des fonctions à l'État et planifie la recomposition
  • Skipping n'est possible que pour les fonctions avec paramètres stables (@Stable ou immuable)
  • Trois déclencheurs de recomposition : changement d'État, changement de paramètres, changement de CompositionLocal
  • List<T> est considérée instable — utilisez des collections immuables pour un skipping correct
  • Layout Inspector affiche les compteurs de recomposition pour chaque fonction
  • Recommandation : extrayez les parties stables de l'UI dans des fonctions séparées et utilisez remember pour les lambdas

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.

Discuter du projet

Lisez aussi