Compose considère les types comme stables ou instables. Un type est stable s'il est immuable ou s'il est possible pour Compose de savoir si sa valeur a changé entre deux recompositions. Un type est instable si Compose ne peut pas savoir si sa valeur a changé entre deux recompositions.
Compose utilise la stabilité des paramètres d'un composable pour déterminer comment comparer les entrées et décider s'il peut ignorer le composable lors de la recomposition (avec le mode de désactivation forcée activé par défaut à partir de Kotlin 2.0.20) :
- Paramètres stables : Compose compare les paramètres stables à l'aide de l'égalité structurelle (
Object.equals()). Si les paramètres stables d'un composable sont égaux à leurs valeurs précédentes, Compose l'ignore. - Paramètres instables : lorsque le mode de désactivation renforcée est activé (par défaut dans Kotlin 2.0.20 et versions ultérieures), Compose compare les paramètres instables à l'aide de l'égalité des instances (
===) et ignore le composable si les mêmes instances d'objet sont transmises. Dans les versions antérieures à Kotlin 2.0.20 (ou lorsque le mode de désactivation forcée est désactivé), Compose recompose toujours un composable avec des paramètres instables lorsque son parent est recomposé.
Si votre application alloue fréquemment de nouvelles instances de paramètres instables ou force des vérifications .equals() coûteuses sur de grandes collections en annotant excessivement les modèles, vous risquez d'observer des recompositions inutiles ou une surcharge de comparaison.
Ce document explique comment Compose détermine la stabilité et comment vous pouvez l'optimiser pour améliorer les performances et l'expérience utilisateur globale.
Objets immuables
Les extraits suivants illustrent les principes généraux de la stabilité et de la recomposition.
La classe Contact est une classe de données immuable. En effet, tous ses paramètres sont des primitives définies avec le mot clé val. Une fois que vous avez créé une instance de Contact, vous ne pouvez pas modifier la valeur des propriétés de l'objet.
Si vous essayez de le faire, vous créerez un nouvel objet.
data class Contact(val name: String, val number: String)
Le composable ContactRow comporte un paramètre de type Contact.
@Composable
fun ContactRow(contact: Contact, modifier: Modifier = Modifier) {
var selected by remember { mutableStateOf(false) }
Row(modifier) {
ContactDetails(contact)
ToggleButton(selected, onToggled = { selected = !selected })
}
}
Voyons ce qui se passe lorsque l'utilisateur clique sur le bouton d'activation et que l'état selected change :
- Compose évalue s'il doit recomposer le code dans
ContactRow. - Il voit que le seul argument de
ContactDetailsest de typeContact. - Comme
Contactest une classe de données immuable, Compose est certain qu'aucun des arguments deContactDetailsn'a changé. - Par conséquent, Compose ignore
ContactDetailset ne le recompose pas. - En revanche, les arguments de
ToggleButtonont changé, et Compose recompose ce composant.
Objets modifiables
Bien que l'exemple précédent utilise un objet immuable, il est possible de créer un objet modifiable. Prenons l'extrait suivant :
data class Contact(var name: String, var number: String)
Comme chaque paramètre de Contact est désormais un var, la classe n'est plus immuable.
Si ses propriétés changeaient, Compose n'en serait pas informé. En effet, Compose ne suit que les modifications apportées aux objets d'état Compose.
Compose considère une telle classe comme instable. Avec le mode d'omission stricte (Kotlin 2.0.20 ou version ultérieure), Compose compare Contact à l'aide de l'égalité d'instance (===) : la mutation de contact.name sur place ne déclenche pas de recomposition, tandis que la transmission d'une instance Contact nouvellement allouée avec des valeurs identiques force toujours ContactDetails à se recomposer (et sans le mode d'omission stricte, ContactDetails se recompose chaque fois que selected change).
Implémentation dans Compose
Il peut être utile, mais pas crucial, de comprendre comment Compose détermine exactement les fonctions à ignorer lors de la recomposition.
Lorsque le compilateur Compose s'exécute sur votre code, il marque chaque fonction et chaque type avec l'un des tags disponibles. Ces tags reflètent la façon dont Compose gère la fonction ou le type lors de la recomposition.
Fonctions
Compose peut marquer les fonctions comme skippable ou restartable. Notez qu'il peut marquer une fonction comme l'une, les deux ou aucune de ces options :
- Ignorable : si le compilateur marque un composable comme pouvant être ignoré, Compose peut l'ignorer lors de la recomposition si tous ses arguments sont égaux à leurs valeurs précédentes. (Avec le mode de désactivation renforcée dans Kotlin 2.0.20 et versions ultérieures, tous les composables redémarrables peuvent être ignorés automatiquement.)
- Redémarrable : un composable redémarrable sert de "champ d'application" où la recomposition peut commencer. En d'autres termes, la fonction peut être un point d'entrée à partir duquel Compose peut commencer à réexécuter le code pour la recomposition après des changements d'état.
Types
Compose marque les types comme immuables ou stables. Chaque type est l'un ou l'autre :
- Immuable : Compose marque un type comme immuable si la valeur de ses propriétés ne peut jamais changer et que toutes les méthodes sont transparentes sur le plan référentiel.
- Notez que tous les types primitifs sont marqués comme immuables. parmi lesquelles :
String,IntetFloat.
- Notez que tous les types primitifs sont marqués comme immuables. parmi lesquelles :
- Stable : indique un type dont les propriétés peuvent changer après la construction. Si ces propriétés changent au moment de l'exécution, Compose en est informé.
Stabilité du débogage
Si votre application recompose un composable dont les paramètres n'ont pas changé, vérifiez d'abord si de nouvelles instances de types instables sont allouées à chaque recomposition (ou, si le mode de saut renforcé est désactivé, si le composable comporte des paramètres avec des propriétés var ou des propriétés val d'un type instable).
Pour en savoir plus sur le diagnostic des problèmes complexes de stabilité dans Compose, consultez le guide Déboguer la stabilité.
Résoudre les problèmes de stabilité
Pour en savoir plus sur la manière d'apporter de la stabilité à votre implémentation Compose, consultez le guide Résoudre les problèmes de stabilité.
Résumé
De manière générale, vous devez noter les points suivants :
- Paramètres : Compose détermine la stabilité de chaque paramètre de vos composables pour décider s'il faut les comparer à l'aide de l'égalité structurelle (
.equals()) ou de l'égalité d'instance (===) lors de la recomposition. - Corrections immédiates : si vous remarquez que votre composable n'est pas ignoré et que cela entraîne un problème de performances, vérifiez si de nouvelles instances de paramètres instables sont recréées à chaque passage ou si des propriétés
varsont utilisées à la place deState. - Rapports du compilateur : vous pouvez utiliser les rapports du compilateur pour déterminer la stabilité déduite de vos classes.
- Collections : Compose considère les interfaces de collection standards (
List,SetetMap) comme instables, car leurs implémentations sous-jacentes peuvent être modifiables. Avec le mode de saut renforcé (activé par défaut dans Kotlin 2.0.20 et versions ultérieures), les collections instables ignorent toujours la recomposition en utilisant l'égalité d'instance (===) lorsque la référence de la collection ne change pas. Soyez prudent lorsque vous remplacez des collections standards par des collections immuables Kotlinx ou que vous annotez des classes contenant des collections avec@Immutableou@Stable, car comparer chaque élément avec.equals()à chaque passe de recomposition peut être plus coûteux que la recomposition elle-même. - Autres modules : Compose considère comme instables les classes des modules dans lesquels le compilateur Compose ne s'exécute pas (comparées à l'aide de
===en mode de saut strict). Si une source de données réinstancie fréquemment de petits modèles plats à partir de modules non Compose avec des valeurs identiques, vous pouvez configurer un fichier de configuration de stabilité ou utiliser@Stableou@Immutableoù.equals()est peu coûteux.
Documentation complémentaire
- Performances : pour obtenir d'autres conseils de débogage sur les performances de Compose, consultez notre guide des bonnes pratiques et notre conférence I/O.