Stabilità nella scrittura

Compose considera i tipi stabili o instabili. Un tipo è stabile se è immutabile o se Compose può sapere se il suo valore è cambiato tra le ricomposizioni. Un tipo è instabile se Compose non può sapere se il suo valore è cambiato tra le ricomposizioni.

Compose utilizza la stabilità dei parametri di un composable per determinare come confrontare gli input e decidere se può saltare il composable durante la ricomposizione (con la modalità di salto rigido attivata per impostazione predefinita a partire da Kotlin 2.0.20):

  • Parametri stabili:Compose confronta i parametri stabili utilizzando l'uguaglianza strutturale (Object.equals()). Se i parametri stabili di un elemento componibile sono uguali ai valori precedenti, Compose lo ignora.
  • Parametri instabili:con la modalità di strong skipping attivata (l'impostazione predefinita in Kotlin 2.0.20 e versioni successive), Compose confronta i parametri instabili utilizzando l'uguaglianza delle istanze (===) e salta l'elemento componibile se vengono passate le stesse istanze di oggetti. Nelle versioni precedenti a Kotlin 2.0.20 (o quando la modalità di salto forte è disattivata), Compose ricompone sempre un composable con parametri instabili quando il relativo elemento principale viene ricomposto.

Se la tua app alloca spesso nuove istanze di parametri instabili o forza controlli .equals() costosi su raccolte di grandi dimensioni tramite l'annotazione eccessiva dei modelli, potresti notare ricomposizioni non necessarie o un sovraccarico di confronto.

Questo documento descrive in dettaglio come Compose determina la stabilità e come puoi ottimizzarla per migliorare le prestazioni e l'esperienza utente complessiva.

Oggetti immutabili

Gli snippet seguenti mostrano i principi generali alla base della stabilità e della ricomposizione.

La classe Contact è una classe di dati immutabile. Questo perché tutti i suoi parametri sono primitive definite con la parola chiave val. Una volta creata un'istanza di Contact, non puoi modificare il valore delle proprietà dell'oggetto. Se tentassi di farlo, creeresti un nuovo oggetto.

data class Contact(val name: String, val number: String)

Il composable ContactRow ha un parametro di tipo Contact.

@Composable
fun ContactRow(contact: Contact, modifier: Modifier = Modifier) {
   var selected by remember { mutableStateOf(false) }

   Row(modifier) {
      ContactDetails(contact)
      ToggleButton(selected, onToggled = { selected = !selected })
   }
}

Considera cosa succede quando l'utente fa clic sul pulsante di attivazione/disattivazione e lo stato di selected cambia:

  1. Compose valuta se deve ricomporre il codice all'interno di ContactRow.
  2. Vede che l'unico argomento per ContactDetails è di tipo Contact.
  3. Poiché Contact è una classe di dati immutabile, Compose è sicuro che nessuno degli argomenti per ContactDetails sia cambiato.
  4. Pertanto, Compose salta ContactDetails e non lo ricompone.
  5. D'altra parte, gli argomenti per ToggleButton sono cambiati e Compose ricompone il componente.

Oggetti modificabili

Anche se l'esempio precedente utilizza un oggetto immutabile, è possibile creare un oggetto mutabile. Considera il seguente snippet:

data class Contact(var name: String, var number: String)

Poiché ogni parametro di Contact è ora un var, la classe non è più immutabile. Se le sue proprietà sono cambiate, Compose non se ne accorgerebbe. Questo perché Compose monitora solo le modifiche agli oggetti State di Compose.

Compose considera questa classe instabile. Con la modalità di salto rigido (Kotlin 2.0.20+), Compose confronta Contact utilizzando l'uguaglianza delle istanze (===): la mutazione di contact.name sul posto non attiverà la ricomposizione, mentre il passaggio di un'istanza di Contact appena allocata con valori identici forzerà comunque la ricomposizione di ContactDetails (e senza la modalità di salto rigido, ContactDetails si ricompone ogni volta che selected cambia).

Implementazione in Compose

Può essere utile, anche se non fondamentale, capire esattamente come Compose determina quali funzioni saltare durante la ricomposizione.

Quando il compilatore Compose viene eseguito sul tuo codice, contrassegna ogni funzione e tipo con uno dei vari tag. Questi tag riflettono il modo in cui Compose gestisce la funzione o il tipo durante la ricomposizione.

Funzioni

Compose può contrassegnare le funzioni come skippable o restartable. Tieni presente che potrebbe contrassegnare una funzione come una, entrambe o nessuna delle seguenti:

  • Ignorabile: se il compilatore contrassegna un elemento componibile come ignorabile, Compose può ignorarlo durante la ricomposizione se tutti i suoi argomenti sono uguali ai valori precedenti. (Con la modalità di salto rigido in Kotlin 2.0.20+, tutti i composable riavviabili sono automaticamente ignorabili.)
  • Riavviabile: un composable riavviabile funge da "ambito" in cui può iniziare la ricomposizione. In altre parole, la funzione può essere un punto di ingresso in cui Compose può iniziare a rieseguire il codice per la ricomposizione dopo le modifiche dello stato.

Tipi

Componi i tipi di indicatori come immutabili o stabili. Ogni tipo è uno o l'altro:

  • Immutabile: Compose contrassegna un tipo come immutabile se il valore delle sue proprietà non può mai cambiare e tutti i metodi sono referenzialmente trasparenti.
    • Tieni presente che tutti i tipi primitivi sono contrassegnati come immutabili. Questi includono String, Int e Float.
  • Stabile: indica un tipo le cui proprietà possono cambiare dopo la costruzione. Se e quando queste proprietà cambiano durante l'esecuzione, Compose ne viene a conoscenza.

Stabilità del debug

Se la tua app ricompone un composable i cui parametri non sono cambiati, controlla innanzitutto se vengono allocate nuove istanze di tipi instabili a ogni ricomposizione (o, se la modalità di salto rigido è disattivata, se il composable ha parametri con proprietà var o val di un tipo instabile).

Per informazioni dettagliate su come diagnosticare problemi complessi di stabilità in Compose, consulta la guida Debug della stabilità.

Risolvere i problemi di stabilità

Per informazioni su come rendere stabile l'implementazione di Compose, consulta la guida Risolvere i problemi di stabilità.

Riepilogo

In generale, tieni presente quanto segue:

  • Parametri: Compose determina la stabilità di ogni parametro dei tuoi composables per decidere se confrontarli utilizzando l'uguaglianza strutturale (.equals()) o l'uguaglianza di istanza (===) durante la ricomposizione.
  • Correzioni immediate: se noti che il composable non viene ignorato e causa un problema di prestazioni, controlla se vengono ricreate nuove istanze di parametri instabili a ogni passaggio o se vengono utilizzate proprietà var anziché State.
  • Report del compilatore: puoi utilizzare i report del compilatore per determinare la stabilità dedotta per le tue classi.
  • Raccolte: Compose considera instabili le interfacce di raccolta standard (List, Set e Map) perché le loro implementazioni sottostanti potrebbero essere mutabili. Con la modalità di salto rigido (attivata per impostazione predefinita in Kotlin 2.0.20+), le raccolte instabili continuano a saltare la ricomposizione utilizzando l'uguaglianza rapida delle istanze O(1) (===) quando il riferimento alla raccolta non cambia. Evita di convertire raccolte di grandi dimensioni in raccolte immutabili Kotlinx o di annotare le classi che contengono raccolte con @Immutable o @Stable, a meno che non sia specificamente richiesta l'uguaglianza strutturale (O(N) .equals()), poiché il confronto di ogni elemento in ogni ricomposizione può essere più costoso della ricomposizione stessa.
  • Altri moduli: Compose considera instabili le classi dei moduli in cui non viene eseguito il compilatore Compose (confronto utilizzando === in modalità di salto rigido). Se un'origine dati istanzia nuovamente di frequente modelli piccoli e piatti da moduli non Compose con valori identici, puoi configurare un file di configurazione della stabilità o utilizzare @Stable o @Immutable dove .equals() è economico.

Per approfondire