Résoudre les problèmes de stabilité

En cas d'utilisation d'une classe instable qui entraîne des performances les problèmes, vous devez le rendre stable. Ce document décrit plusieurs techniques que vous pouvez utiliser pour cela.

Activer les sauts fréquents

Vous devez d'abord essayer d'activer ce mode. Mode "Ignorer" de façon intensive permet d'ignorer les composables avec des paramètres instables. C'est l'option la plus simple pour résoudre les problèmes de performances causés par la stabilité.

Pour en savoir plus, consultez la section Ignorance fortement forte.

Rendre la classe immuable

Vous pouvez également essayer de rendre une classe instable complètement immuable.

  • Immuable: indique un type dans lequel la valeur d'une propriété ne peut jamais sont modifiés après la construction d'une instance de ce type et toutes les méthodes sont de manière référentielle.
    • Assurez-vous que toutes les propriétés de la classe sont val et non var. et de types immuables.
    • Les types primitifs tels que String, Int et Float sont toujours immuables.
    • Si cela est impossible, vous devez utiliser l'état de Compose pour les propriétés modifiables.
  • Stable: indique un type modifiable. L'environnement d'exécution Compose ne savoir si l'une des propriétés ou méthodes publiques du type entraînerait des résultats différents à partir d'un appel précédent.

Collections immuables

Les collections sont l'une des raisons courantes pour lesquelles 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 sûr que les collections telles que List, Map et Set sont vraiment immuables et les marque donc comme instables.

Pour résoudre ce problème, vous pouvez utiliser des collections immuables. Compilateur Compose est compatible avec les collections immuables Kotlinx. Ces les collections sont immuables, et le compilateur Compose les traite en tant que tel. Cette bibliothèque étant encore en version alpha, attendez-vous à des modifications de son API.

Considérez à nouveau cette classe instable de l'atelier Diagnostiquer la stabilité problèmes:

unstable class Snack {
  
  unstable val tags: Set<String>
  
}

Vous pouvez rendre tags stable à l'aide d'une collection immuable. Dans le cours, remplacez Type de tags sur 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 Compose le compilateur 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 aurait autrement déduire de votre classe. Elle est semblable à la !! en Kotlin. Vous devez être très soyez prudent lorsque vous utilisez ces annotations. Remplacer le comportement du compilateur peut entraîner des bugs imprévus, tels que votre composable qui ne se recompose pas comme prévu.

S'il est possible de rendre votre classe stable sans annotation, vous devez s'efforcer d'atteindre la stabilité de cette façon.

L'extrait de code suivant fournit un exemple minimal de classe de données annotée en tant que immuables:

@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

Prenons l'exemple d'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 étant instable.

Les paramètres sont confrontés au même problème que les classes concernant les types de collections, Le compilateur Compose marque toujours un paramètre de type List comme instable, même lorsqu'il s'agit d'un ensemble de types stables.

Vous ne pouvez pas marquer un paramètre individuel comme stable, ni annoter un pour qu'il soit toujours ignorable. Il y a plusieurs façons d'avancer.

Il existe plusieurs façons de contourner le problème des collections instables. Ces différentes approches sont décrites dans les sous-sections suivantes.

Fichier de configuration

Si vous souhaitez respecter le contrat de stabilité dans votre codebase, alors vous pouvez considérer les collections Kotlin comme stables en ajoutant kotlin.collections.* à votre fichier de configuration de stabilité.

Collection immuable

Pour garantir l'immuabilité au moment de la compilation, vous pouvez utilisez 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 créer la vôtre. Pour ce faire, Encapsulez List dans une classe stable annotée. Un wrapper générique est probablement le meilleur choix 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 à présent Composable HighlightedSnacks en tant que 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 ont changé.

Fichier de configuration de la stabilité

À partir de Compose Compiler 1.5.5, un fichier de configuration des classes comme stable peut être fournie au moment de la compilation. Cela permet d'envisager les classes que vous ne contrôlez pas, telles que les classes de bibliothèque standards ; comme LocalDateTime, comme étant stable.

Le fichier de configuration est un fichier au format texte brut contenant une classe par ligne. Commentaires les caractères génériques simples et doubles sont acceptés. Voici un exemple de configuration:

// Consider LocalDateTime stable
java.time.LocalDateTime
// Consider kotlin collections stable
kotlin.collections.*
// 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 du fichier de configuration à Compose les options du compilateur.

Groovy

kotlinOptions {
    freeCompilerArgs += [
            "-P",
            "plugin:androidx.compose.compiler.plugins.kotlin:stabilityConfigurationPath=" +
                    project.absolutePath + "/compose_compiler_config.conf"
    ]
}

Kotlin

kotlinOptions {
  freeCompilerArgs += listOf(
      "-P",
      "plugin:androidx.compose.compiler.plugins.kotlin:stabilityConfigurationPath=" +
      "${project.absolutePath}/compose_compiler_config.conf"
  )
}

Comme le compilateur Compose s'exécute sur chaque module de votre projet séparément, vous pouvez fournir différentes configurations pour différents modules si nécessaire. Vous pouvez aussi en avoir un au niveau racine de votre projet et de transmettre ce chemin d'accès de ce module.

Plusieurs modules

Un autre problème courant concerne l'architecture multimodule. Compilateur Compose ne peut déterminer si une classe est stable que si tous les types non primitifs qui auxquelles il fait référence sont soit explicitement marquées comme stables, soit dans un module qui a été également créé avec le compilateur Compose.

Si votre couche de données se trouve dans un module distinct de votre couche d'UI, qui correspond au l'approche recommandée, il peut s'agir d'un problème que vous rencontrez.

Solution

Pour résoudre ce problème, vous pouvez adopter l'une des approches suivantes:

  1. Ajouter les classes à votre fichier de configuration de compilateur.
  2. Activez le compilateur Compose sur vos modules de couche de données ou ajoutez des tags à vos classes avec @Stable ou @Immutable, le cas échéant.
    • Cela implique d'ajouter une dépendance Compose à votre couche de données. Toutefois, il s'agit simplement de la dépendance pour l'environnement d'exécution Compose, et non pour Compose-UI
  3. Dans votre module d'UI, encapsulez vos classes de couche de données dans un wrapper spécifique à l'UI. classes.

Le même problème se produit lors de l'utilisation de bibliothèques externes si celles-ci n'utilisent pas Compilateur Compose.

Tous les composables ne doivent pas être désactivables

Lorsque vous essayez de résoudre les problèmes de stabilité, vous ne devez pas essayer de composable désactivable. Toute tentative d'optimisation peut entraîner une optimisation prématurée. qui introduit plus de problèmes qu’il n’en résout.

Dans de nombreuses situations, le choix d'une annonce désactivable ne présente aucun avantage réel. et peut rendre le code difficile à gérer. Exemple :

  • Un composable qui n'est pas souvent recomposé, voire pas du tout.
  • Un composable qui, en soi, appelle simplement des composables désactivables.
  • Un composable avec un grand nombre de paramètres avec cher est égal à mises en œuvre. Dans ce cas, le coût de la vérification si un paramètre a pourraient l'emporter sur le coût d'une recomposition bon marché.

Lorsqu'un composable est désactivable, cela ajoute une légère surcharge qui n'en vaut peut-être pas la peine. Vous pouvez même annoter votre composable de sorte qu'il ne soit pas redémarrable dans les cas où vous déterminez que le fait d'être redémarré est plus coûteux que ça ne vaut le coup.