Compose considère les types comme stables ou instables. Un type est stable s'il est immuable ou si Compose peut 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 d'ignorance stricte 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 forcé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 se recompose.
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 sur-annotant les modèles, vous pouvez observer des recompositions ou des frais de comparaison inutiles.
Ce document explique en détail 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 plus modifier la valeur des propriétés de l'objet.
Si vous essayiez de le faire, vous créeriez 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 })
}
}
Réfléchissez à 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 constate que le seul argument de
ContactDetailsest de typeContact. - Comme
Contactest une classe de données immuable, Compose est sûr 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)
Étant donné que chaque paramètre de Contact est désormais un var, la classe n'est plus immuable.
Si ses propriétés changeaient, Compose ne le saurait pas. En effet, Compose ne suit que les modifications apportées aux objets d'état Compose.
Compose considère qu'une telle classe est instable. Avec le mode de désactivation 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 le transfert d'une instance Contact nouvellement allouée avec des valeurs identiques forcera toujours ContactDetails à se recomposer (et sans le mode de désactivation stricte, ContactDetails se recompose chaque fois que selected change).
Implémentation dans Compose
Il peut être utile, mais pas indispensable, de réfléchir à la manière dont 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 suivants. 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 étant l'une ou l'autre de ces catégories, ou aucune d'elles :
- Ignorable : si le compilateur marque un composable comme ignorable, 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 désactivés automatiquement.)
- Redémarrable : un composable redémarrable sert de "portée" où la recomposition peut commencer. En d'autres termes, la fonction peut être un point d'entrée à partir duquel Compose peut recommencer à exécuter du 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 si toutes les méthodes sont référentiellement transparentes.
- Notez que tous les types primitifs sont marqués comme immuables. Il peut s'agir de
String,IntetFloat.
- Notez que tous les types primitifs sont marqués comme immuables. Il peut s'agir de
- Stable : indique un type dont les propriétés peuvent changer après la construction. Si et quand 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 fort est désactivé, si le composable comporte des paramètres avec des propriétés var ou val d'un type instable).
Pour en savoir plus sur le diagnostic des problèmes de stabilité complexes dans Compose, consultez le guide Déboguer la stabilité.
Résoudre les problèmes de stabilité
Pour savoir comment apporter de la stabilité à votre implémentation Compose, consultez le guide Résoudre les problèmes de stabilité.
Résumé
En général, vous devez tenir compte des 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 qu'il provoque 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é inférée de vos classes.
- Collections : Compose considère les interfaces de collection standards (
List,SetetMap) comme instables, car leurs implémentations sous-jacentes peuvent être mutables. Avec le mode de désactivation stricte (activé par défaut dans Kotlin 2.0.20+), les collections instables désactivent toujours la recomposition à l'aide de l'égalité d'instanceO(1)rapide (===) lorsque la référence de la collection ne change pas. Évitez de convertir de grandes collections en collections immuables Kotlinx ou d'annoter les classes contenant des collections avec@Immutableou@Stable, sauf si l'égalité structurelle (O(N).equals()) est spécifiquement requise, car la comparaison de chaque élément à chaque recomposition peut être plus coûteuse que la recomposition elle-même. - Autres modules : Compose considère que les classes des modules où le compilateur Compose ne s'exécute pas sont instables (comparaison à l'aide de
===en mode de saut fort). 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 bon marché.
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.