Risolvi i problemi di stabilità

Di fronte a una classe instabile che causa prestazioni problemi, devi renderla stabile. Questo documento illustra diverse tecniche che può utilizzare per farlo.

Attiva l'opzione Ignora forte

Dovresti prima provare ad attivare la modalità di salto forte. Modalità di salto potente consente di saltare gli elementi componibili con parametri instabili ed è il metodo per risolvere i problemi di prestazioni causati dalla stabilità.

Per ulteriori informazioni, consulta la sezione Saltare in modo forzato.

Rendi il corso immutabile

Puoi anche provare a rendere una classe instabile del tutto immutabile.

  • Immutabile: indica un tipo in cui il valore di qualsiasi proprietà non può mai modificare dopo la creazione di un'istanza di quel tipo e tutti i metodi vengono referenzialmente trasparenti.
    • Assicurati che tutte le proprietà della classe siano entrambe val anziché var, e di tipi immutabili.
    • I tipi primitivi come String, Int e Float sono sempre immutabili.
    • Se non è possibile, devi utilizzare lo stato di composizione per proprietà modificabili.
  • Stabile: indica un tipo modificabile. Il runtime di Compose venire a conoscenza se e quando qualsiasi proprietà o metodo pubblico del tipo questo comportamento produrrà risultati diversi rispetto a una chiamata precedente.
di Gemini Advanced.

Raccolte immutabili

Un motivo comune per cui Compose considera una classe instabile sono le raccolte. Come indicato Nella pagina Diagnosticare i problemi di stabilità, il compilatore Compose non possono essere completamente sicure che raccolte quali List, Map e Set siano immutabili e di conseguenza li contrassegna come instabili.

Per risolvere questo problema, puoi utilizzare le raccolte immutabili. Compilatore Compose include il supporto per le Raccolte immutabili di Kotlinx. Questi Le raccolte sono garantite per essere immutabili e il compilatore Compose le tratta come tale. Questa libreria è ancora in versione alpha, quindi sono previste possibili modifiche alla relativa API.

Considera di nuovo questa classe instabile dalla sezione Diagnostica di stabilità ai problemi:

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

Puoi rendere stabile tags utilizzando una raccolta immutabile. In classe, cambia tipo di tags a ImmutableSet<String>:

data class Snack{
    …
    val tags: ImmutableSet<String> = persistentSetOf()
    …
}

Dopodiché, tutti i parametri della classe sono immutabili e il comando Compose il compilatore contrassegna la classe come stabile.

Aggiungi annotazioni con Stable o Immutable

Un possibile percorso per risolvere i problemi di stabilità è annotare le classi instabili con @Stable o @Immutable.

L'annotazione di una classe ha la precedenza su ciò che altrimenti verrebbe fatto dal compilatore inferire sul tuo corso. È simile !! operatore in Kotlin. Dovresti essere molto fai attenzione a come usi queste annotazioni. Override del comportamento del compilatore potresti incorrere in bug imprevisti, come la mancata ricomposizione del componibile come ti aspetti.

Se è possibile rendere stabile la classe senza un'annotazione, dovresti cercare di raggiungere la stabilità in questo modo.

Lo snippet seguente fornisce un esempio minimo di una classe di dati annotata come immutabile:

@Immutable
data class Snack(
…
)

Sia che utilizzi l'annotazione @Immutable o @Stable, il compilatore Compose contrassegna la classe Snack come stabile.

Classi annotate nelle raccolte

Considera un componibile che include un parametro di tipo List<Snack>:

restartable scheme("[androidx.compose.ui.UiComposable]") fun HighlightedSnacks(
  …
  unstable snacks: List<Snack>
  …
)

Anche se annota Snack con @Immutable, il compilatore Compose contrassegna comunque il parametro snacks in HighlightedSnacks come instabile.

I parametri devono affrontare lo stesso problema delle classi per quanto riguarda i tipi di raccolta. il compilatore Compose contrassegna sempre un parametro di tipo List come instabile, anche quando si tratta di una raccolta di tipi stabili.

Non puoi contrassegnare un singolo parametro come stabile, né puoi annotare un componibili in modo che siano sempre ignorabili. Sono presenti più percorsi per il progresso.

Esistono diversi modi per risolvere il problema delle raccolte instabili. Le seguenti sottosezioni descrivono questi diversi approcci.

File di configurazione

Se vuoi rispettare il contratto di stabilità nel tuo codebase, puoi scegliere di considerare stabili le raccolte Kotlin aggiungendo kotlin.collections.* al tuo file di configurazione della stabilità.

Raccolta immutabile

Per la sicurezza dell'immutabilità in fase di compilazione, puoi utilizza una raccolta immutabile di kotlinx anziché List.

@Composable
private fun HighlightedSnacks(
    …
    snacks: ImmutableList<Snack>,
    …
)

Wrapper

Se non puoi utilizzare una raccolta immutabile, puoi crearne una personalizzata. Per farlo, aggrega List in una classe stabile annotata. Un wrapper generico è probabilmente la scelta migliore, a seconda delle tue esigenze.

@Immutable
data class SnackCollection(
   val snacks: List<Snack>
)

Questo valore può essere utilizzato come tipo di parametro nel componibile.

@Composable
private fun HighlightedSnacks(
    index: Int,
    snacks: SnackCollection,
    onSnackClick: (Long) -> Unit,
    modifier: Modifier = Modifier
)

Soluzione

Dopo aver seguito uno di questi approcci, il compilatore Compose ora contrassegna HighlightedSnacks Componibile sia come skippable che come 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
)

Durante la ricomposizione, ora Compose può saltare HighlightedSnacks se nessuno dei suoi sono cambiati.

File di configurazione della stabilità

A partire da Compose Compiler 1.5.5, un file di configurazione di classi che può essere fornito in fase di compilazione. Ciò consente di considerare che non controlli, come le classi della libreria standard come LocalDateTime, come stabile.

Il file di configurazione è un file di testo normale con una classe per riga. Commenti sono supportati i caratteri jolly singoli e doppi. Di seguito è riportato un esempio di configurazione:

// 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<*,_>

Per attivare questa funzionalità, passa il percorso del file di configurazione a Compose del compilatore.

Alla moda

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"
  )
}

Poiché il compilatore Compose viene eseguito separatamente su ciascun modulo del tuo progetto, puoi fornire configurazioni diverse ai vari moduli, se necessario. In alternativa, crea un a livello principale del progetto e trasmetti il percorso a ogni in maggior dettaglio più avanti in questo modulo.

Più moduli

Un altro problema comune riguarda l'architettura multimodulo. Compilatore Compose può dedurre se una classe è stabile solo se tutti i tipi non primitivi che i riferimenti sono contrassegnati esplicitamente come stabili o in un modulo anche creato con il compilatore Compose.

Se il livello dati si trova in un modulo separato da quello dell'interfaccia utente, ovvero potrebbe essere un problema che potresti riscontrare.

Soluzione

Per risolvere il problema, puoi adottare uno dei seguenti approcci:

  1. Aggiungi i corsi al file di configurazione del compilatore.
  2. Attiva il compilatore Compose nei moduli del livello dati o tagga i tuoi corsi con @Stable o @Immutable, ove opportuno.
    • Ciò comporta l'aggiunta di una dipendenza Compose al livello dati. Tuttavia, si tratta solo della dipendenza per il runtime di Compose e non per Compose-UI.
  3. All'interno del modulo UI, aggrega le classi del livello dati in un wrapper specifico per l'interfaccia utente. .

Lo stesso problema si verifica anche quando si utilizzano librerie esterne se non utilizzano Crea il compilatore.

Non tutti i componibili devono essere ignorabili

Quando lavori per risolvere problemi di stabilità, non cercare di risolvere ogni ignorabile componibile. Cercare di farlo può portare a un'ottimizzazione prematura che introduce più problemi che come risolvere.

Ci sono molte situazioni in cui essere ignorabili non offre alcun vantaggio reale e può causare complicazioni di manutenzione. Ad esempio:

  • Un componibile che non viene ricomposto spesso o affatto.
  • Un componibile che di per sé chiama semplicemente elementi componibili ignorabili.
  • Un componibile con un gran numero di parametri con conversioni costose implementazioni. In questo caso, il costo del controllo di eventuali parametri può superare il costo di una ricomposizione economica.

Quando un componibile è ignorabile, aggiunge un piccolo overhead che potrebbe non valere li annotino. Puoi persino annotare il tuo componibile in modo che sia non riavviabile nei casi in cui determini che il riavvio è più overhead di quanto valga.