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 avanzata 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 componibile sono uguali ai valori precedenti, Compose li ignora. - Parametri instabili: con la modalità di strong skipping attivata (impostazione predefinita in Kotlin 2.0.20 e versioni successive), Compose confronta i parametri instabili utilizzando l'uguaglianza delle istanze (
===) e salta il componibile se vengono passate le stesse istanze dell'oggetto. Nelle versioni precedenti a Kotlin 2.0.20 (o quando la modalità di strong skipping è 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 annotando eccessivamente i modelli, potresti osservare ricomposizioni non necessarie o un overhead 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 provassi a farlo, creeresti un nuovo oggetto.
data class Contact(val name: String, val number: String)
Il componibile 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:
- Compose valuta se deve ricomporre il codice all'interno di
ContactRow. - Rileva che l'unico argomento per
ContactDetailsè di tipoContact. - Poiché
Contactè una classe di dati immutabile, Compose è sicuro che nessuno degli argomenti diContactDetailssia cambiato. - Di conseguenza, Compose salta
ContactDetailse non lo ricompone. - D'altra parte, gli argomenti per
ToggleButtonsono cambiati e Compose ricompone quel componente.
Oggetti modificabili
Sebbene l'esempio precedente utilizzi 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à cambiassero, Compose non lo saprebbe. Questo perché Compose monitora solo le modifiche agli oggetti State di Compose.
Compose considera questa classe instabile. Con la modalità di salto avanzata
(Kotlin 2.0.20+), Compose confronta Contact utilizzando l'uguaglianza delle istanze (===):
la mutazione di contact.name in loco non attiverà la ricomposizione, mentre il passaggio di
un'istanza Contact appena allocata con valori identici forzerà comunque
la ricomposizione di ContactDetails (e senza la modalità di salto avanzata,
ContactDetails si ricompone ogni volta che selected cambia).
Implementazione in Compose
Può essere utile, anche se non fondamentale, considerare 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 di queste:
- Ignorabile: se il compilatore contrassegna un composable come ignorabile, Compose può ignorarlo durante la ricomposizione se tutti i suoi argomenti sono uguali ai valori precedenti. (Con la modalità di strong skipping in Kotlin 2.0.20 e versioni successive, tutti gli elementi componibili 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
Compose contrassegna i tipi 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,InteFloat.
- Tieni presente che tutti i tipi primitivi sono contrassegnati come immutabili. Questi includono
- Stabile: indica un tipo le cui proprietà possono cambiare dopo la costruzione. Se e quando queste proprietà cambiano durante il runtime, Compose rileva queste modifiche.
Esegui il debug della stabilità
Se la tua app sta ricomponendo un composable i cui parametri non sono cambiati, verifica innanzitutto se vengono allocate nuove istanze di tipi instabili a ogni ricomposizione (o, se la modalità di skipping forte è disabilitata, se il composable ha parametri con proprietà var o proprietà val di un tipo instabile).
Per informazioni dettagliate su come diagnosticare problemi complessi di stabilità in Compose, consulta la guida Esegui il debug della stabilità.
Risolvi i problemi di stabilità
Per informazioni su come garantire la stabilità dell'implementazione di Compose, consulta la guida Risolvi i problemi di stabilità.
Riepilogo
In generale, tieni presente quanto segue:
- Parametri: Compose determina la stabilità di ogni parametro dei tuoi
componibili per decidere se confrontarli utilizzando l'uguaglianza strutturale
(
.equals()) o l'uguaglianza delle istanze (===) durante la ricomposizione. - Correzioni immediate: se noti che il tuo composable non viene ignorato e
questo causa un problema di prestazioni, controlla se vengono ricreate nuove istanze di parametri
instabili a ogni passaggio o se vengono utilizzate
proprietà
varanzichéState. - Report del compilatore: puoi utilizzare i report del compilatore per determinare quale stabilità viene dedotta per le tue classi.
- Raccolte: Compose considera instabili le interfacce di raccolta standard (
List,SeteMap) perché le loro implementazioni sottostanti potrebbero essere mutabili. Con la modalità di salto avanzata (attivata per impostazione predefinita in Kotlin 2.0.20 e versioni successive), le raccolte instabili continuano a saltare la ricomposizione utilizzando l'uguaglianza delle istanze (===) quando il riferimento alla raccolta non cambia. Fai attenzione a sostituire le raccolte standard con raccolte immutabili Kotlinx o ad annotare le classi contenenti raccolte con@Immutableo@Stable, poiché confrontare ogni elemento con.equals()in ogni passaggio di ricomposizione può essere più costoso della ricomposizione stessa. - Altri moduli: Compose considera instabili le classi dei moduli in cui il compilatore Compose non viene eseguito (confrontate utilizzando
===in modalità di skipping avanzato). Se un'origine dati crea spesso istanze di modelli piccoli e piatti da moduli non Compose con valori identici, puoi configurare un file di configurazione della stabilità o utilizzare@Stableo@Immutabledove.equals()è economico.
Per approfondire
- Prestazioni: per ulteriori suggerimenti sul debug delle prestazioni di Compose, consulta la nostra guida alle best practice e il talk di I/O.