Lorsque vous êtes confronté à une classe instable qui entraîne des problèmes de performances, vous devez la rendre stable. Ce document décrit plusieurs techniques que vous pouvez utiliser pour ce faire.
Activer la désactivation renforcée
Vous devez d'abord essayer d'activer le mode de désactivation renforcée. Ce mode permet d'ignorer les composables avec des paramètres instables. Il s'agit de la méthode la plus simple pour résoudre les problèmes de performances causés par la stabilité.
Pour en savoir plus, consultez la section Désactivation renforcée.
Rendre la classe immuable
Vous pouvez également essayer de rendre une classe instable complètement immuable.
- Immuable : indique un type dont la valeur des propriétés ne peut jamais
changer après la construction d'une instance de ce type, et toutes les méthodes sont
référentiellement transparentes.
- Assurez-vous que toutes les propriétés de la classe sont à la fois
valplutôt quevar, et de types immuables. - Les types primitifs tels que
String, IntetFloatsont toujours immuables. - Si cela n'est pas possible, vous devez utiliser l'état Compose pour toutes les propriétés modifiables.
- Assurez-vous que toutes les propriétés de la classe sont à la fois
- Stable : indique un type modifiable. L'environnement d'exécution Compose ne sait pas si et quand le comportement des propriétés publiques ou des méthodes du type générerait des résultats différents d'un appel précédent.
Collections immuables
Les collections sont une raison courante pour laquelle Compose considère une classe comme instable. Comme indiqué
sur la page Diagnostiquer les problèmes de stabilité, le compilateur Compose
ne peut pas être complètement sûr que les collections telles que List, Map, et Set sont
réellement immuables et les marque donc comme instables.
Pour résoudre ce problème, vous pouvez utiliser des collections immuables. Le compilateur Compose est compatible avec les collections immuables Kotlinx. Ces collections sont conçues pour être immuables, et le compilateur Compose les traite comme telles. Cette bibliothèque est toujours en version alpha. Des modifications peuvent donc être apportées à son API.
Considérez à nouveau cette classe instable du guide Diagnostiquer les problèmes de stabilité :
unstable class Snack {
…
unstable val tags: Set<String>
…
}
Vous pouvez rendre tags stable à l'aide d'une collection immuable. Dans la classe, remplacez
le type de tags par ImmutableSet<String> :
data class Snack{
…
val tags: ImmutableSet<String> = persistentSetOf()
…
}
Une fois cette opération effectuée, tous les paramètres de la classe sont immuables, et le compilateur Compose marque la classe comme stable.
Annoter avec Stable ou Immutable
Pour résoudre les problèmes de stabilité, vous pouvez annoter les classes instables avec @Stable ou @Immutable.
L'annotation d'une classe remplace ce que le compilateur déduirait autrement
déduire de votre classe. Elle est semblable à l'
!! opérateur en Kotlin. Vous devez être très prudent quant à la façon dont vous utilisez ces annotations. Le remplacement du comportement du compilateur peut entraîner des bugs imprévus, par exemple si votre composable ne se recompose pas lorsque vous vous y attendez.
S'il est possible de rendre votre classe stable sans annotation, vous devez vous efforcer d'atteindre la stabilité de cette manière.
L'extrait suivant fournit un exemple minimal d'une classe de données annotée comme immuable :
@Immutable
data class Snack(
…
)
Que vous utilisiez l'annotation @Immutable ou @Stable, le compilateur Compose marque la classe Snack comme stable.
Classes annotées dans les collections
Considérez un composable qui inclut un paramètre de type List<Snack> :
restartable scheme("[androidx.compose.ui.UiComposable]") fun HighlightedSnacks(
…
unstable snacks: List<Snack>
…
)
Même si vous annotez Snack avec @Immutable, le compilateur Compose marque toujours
le paramètre snacks dans HighlightedSnacks comme instable.
Les paramètres sont confrontés au même problème que les classes en ce qui concerne les types de collection,
le compilateur Compose marque toujours un paramètre de type List comme instable, même
lorsqu'il s'agit d'une collection de types stables.
Vous ne pouvez pas marquer un paramètre individuel comme stable, ni annoter un composable pour qu'il soit toujours désactivable. Il existe plusieurs façons de procéder.
Vous pouvez contourner le problème des collections instables de plusieurs manières. Les sous-sections suivantes décrivent ces différentes approches.
Fichier de configuration
Si vous acceptez de respecter le contrat de stabilité dans votre code, vous
pouvez choisir de considérer les collections Kotlin comme stables en ajoutant
kotlin.collections.* à votre fichier de configuration de stabilité.
Collection immuable
Pour la sécurité de l'immuabilité au temps de compilation, vous pouvez utiliser une collection immuable kotlinx au lieu de List.
@Composable
private fun HighlightedSnacks(
…
snacks: ImmutableList<Snack>,
…
)
Wrapper
Si vous ne pouvez pas utiliser de collection immuable, vous pouvez en créer une. Pour ce faire, encapsulez la List dans une classe stable annotée. Un wrapper générique est probablement le meilleur choix pour cela, en fonction de vos besoins.
@Immutable
data class SnackCollection(
val snacks: List<Snack>
)
Vous pouvez ensuite l'utiliser comme type de paramètre dans votre composable.
@Composable
private fun HighlightedSnacks(
index: Int,
snacks: SnackCollection,
onSnackClick: (Long) -> Unit,
modifier: Modifier = Modifier
)
Solution
Après avoir adopté l'une de ces approches, le compilateur Compose marque désormais le composable HighlightedSnacks comme skippable et restartable.
restartable skippable scheme("[androidx.compose.ui.UiComposable]") fun HighlightedSnacks(
stable index: Int
stable snacks: ImmutableList<Snack>
stable onSnackClick: Function1<Long, Unit>
stable modifier: Modifier? = @static Companion
)
Lors de la recomposition, Compose peut désormais ignorer HighlightedSnacks si aucune de ses entrées n'a changé.
Fichier de configuration de la stabilité
À partir du compilateur Compose 1.5.5, un fichier de configuration des classes à considérer comme stables peut être fourni au temps de compilation. Cela permet de considérer comme stables les classes que vous ne contrôlez pas, telles que les classes de bibliothèque standard comme LocalDateTime.
Le fichier de configuration est un fichier en texte brut contenant une classe par ligne. Les commentaires, les caractères génériques simples et doubles sont acceptés.
Exemple de configuration :
// Consider LocalDateTime stable
java.time.LocalDateTime
// Consider my datalayer stable
com.datalayer.*
// Consider my datalayer and all submodules stable
com.datalayer.**
// Consider my generic type stable based off it's first type parameter only
com.example.GenericClass<*,_>
Pour activer cette fonctionnalité, transmettez le chemin d'accès au fichier de configuration au
composeCompiler bloc d'options de la configuration du plug-in Gradle du compilateur Compose.
composeCompiler {
stabilityConfigurationFile = rootProject.layout.projectDirectory.file("stability_config.conf")
}
Étant donné que le compilateur Compose s'exécute séparément sur chaque module de votre projet, vous pouvez fournir différentes configurations à différents modules si nécessaire. Vous pouvez également avoir une configuration au niveau racine de votre projet et transmettre ce chemin d'accès à chaque module.
Plusieurs modules
Un autre problème courant concerne l'architecture multi-modules. Le compilateur Compose ne peut déduire si une classe est stable que si tous les types non primitifs auxquels elle fait référence sont explicitement marqués comme stables ou dans un module qui a également été créé avec le compilateur Compose.
Si votre couche de données se trouve dans un module distinct de votre couche d'interface utilisateur, ce qui est l'approche recommandée, vous pouvez rencontrer ce problème.
Solution
Pour résoudre ce problème, vous pouvez adopter l'une des approches suivantes :
- Ajoutez les classes à votre fichier de configuration du compilateur.
- Activez le compilateur Compose sur vos modules de couche de données ou balisez vos classes avec
@Stableou@Immutablele cas échéant.- Cela implique d'ajouter une dépendance Compose à votre couche de données. Toutefois, il ne s'agit que de la dépendance pour l'environnement d'exécution Compose et non pour
Compose-UI.
- Cela implique d'ajouter une dépendance Compose à votre couche de données. Toutefois, il ne s'agit que de la dépendance pour l'environnement d'exécution Compose et non pour
- Dans votre module d'interface utilisateur, encapsulez vos classes de couche de données dans des classes de wrapper spécifiques à l'interface utilisateur.
Le même problème se produit également lorsque vous utilisez des bibliothèques externes si elles n'utilisent pas le compilateur Compose.
Tous les composables ne doivent pas être désactivables
Lorsque vous résolvez des problèmes de stabilité, vous ne devez pas essayer de rendre chaque composable désactivable. Si vous essayez de le faire, vous risquez d'effectuer une optimisation prématurée qui introduit plus de problèmes qu'elle n'en résout.
Dans de nombreuses situations, le fait d'être désactivable n'a aucun avantage réel et peut entraîner un code difficile à maintenir. Exemple :
- Un composable qui n'est pas recomposé souvent, voire pas du tout.
- Un composable qui appelle lui-même des composables désactivables.
- Un composable avec un grand nombre de paramètres et des implémentations d'égalité coûteuses. Dans ce cas, le coût de la vérification de la modification d'un paramètre peut être supérieur au coût d'une recomposition peu coûteuse.
Lorsqu'un composable est désactivable, il ajoute une petite surcharge qui peut ne pas en valoir la peine. Vous pouvez même annoter votre composable pour qu'il soit non redémarrable dans les cas où vous déterminez que le fait d'être redémarrable représente une surcharge plus importante que ce qu'il vaut.