Panoramica di ViewModel Parte di Android Jetpack.
La classe ViewModel è un holder di stato a livello di logica di business o schermata. Espone lo stato all'UI e incapsula la logica di business correlata.
Il suo vantaggio principale è che memorizza nella cache lo stato e lo mantiene durante le modifiche alla configurazione. Ciò significa che l'UI non deve recuperare di nuovo i dati quando si passa da un'attività all'altra o in seguito a modifiche alla configurazione, ad esempio quando si ruota lo schermo.
Per ulteriori informazioni sugli holder di stato, consulta la guida sugli holder di stato. Allo stesso modo, per ulteriori informazioni sul livello UI in generale, consulta la guida al livello UI.
Vantaggi di ViewModel
L'alternativa a ViewModel è una classe semplice che contiene i dati visualizzati nell'UI. Questo può diventare un problema quando si passa da un'attività all'altra o da una destinazione di navigazione all'altra. In questo caso, i dati vengono eliminati se non li memorizzi utilizzando il meccanismo dello stato dell'istanza salvata. ViewModel fornisce una comoda API per la persistenza dei dati che risolve questo problema.
In alternativa, per gli holder di stato puri, Compose offre funzionalità retain che consentono alle classi semplici di sopravvivere alle modifiche alla configurazione senza l'infrastruttura completa di un ViewModel. Sebbene entrambi i meccanismi aiutino a mantenere lo stato, in genere è più sicuro fornire un ViewModel a un'istanza mantenuta piuttosto che il contrario, poiché i loro cicli di vita e i comportamenti di pulizia sono diversi.
I principali vantaggi della classe ViewModel sono essenzialmente due:
- Consente di mantenere lo stato dell'UI.
- Fornisce l'accesso alla logica di business.
Persistenza
ViewModel consente la persistenza sia tramite lo stato che un ViewModel contiene sia tramite le operazioni che un ViewModel attiva. Questa memorizzazione nella cache significa che non devi recuperare di nuovo i dati tramite modifiche comuni alla configurazione, ad esempio una rotazione dello schermo.
Ambito
Quando crei un'istanza di ViewModel, gli passi un oggetto che implementa l'interfaccia
ViewModelStoreOwner. Può trattarsi di una destinazione di navigazione, di un grafico di navigazione, di un'attività o di qualsiasi altro tipo che implementi l'interfaccia. Puoi anche definire l'ambito di un ViewModel direttamente in un elemento componibile utilizzando
l'API rememberViewModelStoreOwner.
Il ViewModel viene quindi definito nell'ambito del ciclo di vita di
ViewModelStoreOwner. Rimane in memoria finché il relativo ViewModelStoreOwner non viene eliminato definitivamente (ad esempio quando il proprietario dell'elemento componibile esce dalla composizione).
Una serie di classi sono sottoclassi dirette o indirette dell'interfaccia ViewModelStoreOwner. Le sottoclassi dirette sono
ComponentActivity e NavBackStackEntry.
Per un elenco completo delle sottoclassi indirette, consulta il
ViewModelStoreOwner riferimento. Per definire l'ambito di ViewModel per singoli elementi in un LazyList o Pager, utilizza rememberViewModelStoreProvider() per spostare la gestione del proprietario all'elemento padre.
Quando l'attività host subisce una modifica alla configurazione, il lavoro asincrono continua in ViewModel, indipendentemente dal fatto che sia definito nell'ambito dell'attività o di un elemento componibile specifico. Questa è la chiave della persistenza.
Per ulteriori informazioni, consulta la sezione relativa al ciclo di vita di ViewModel che segue, le API di definizione dell'ambito di ViewModel, e la guida sull'innalzamento dello stato per Jetpack Compose.
SavedStateHandle
SavedStateHandle consente di mantenere i dati non solo durante le modifiche alla configurazione, ma anche in caso di interruzione del processo. Ciò significa che puoi mantenere intatto lo stato dell'UI anche quando l'utente chiude l'app e la riapre in un secondo momento.
Per ulteriori informazioni sul salvataggio dello stato dell'UI, consulta Salvare lo stato dell'UI in Compose.
Accesso alla logica di business
Anche se la stragrande maggioranza della logica di business è presente nel livello dati, anche il livello UI può contenere logica di business. Questo può accadere quando si combinano dati provenienti da più repository per creare lo stato dell'UI della schermata o quando un tipo di dati specifico non richiede un livello dati.
ViewModel è il posto giusto per gestire la logica di business nel livello UI. ViewModel è anche responsabile della gestione degli eventi e della loro delega ad altri livelli della gerarchia quando è necessario applicare la logica di business per modificare i dati dell'applicazione.
Implementare un ViewModel
Di seguito è riportato un esempio di implementazione di un ViewModel per una schermata che consente all'utente di tirare i dadi.
data class DiceUiState(
val firstDieValue: Int? = null,
val secondDieValue: Int? = null,
val numberOfRolls: Int = 0,
)
class DiceRollViewModel : ViewModel() {
// Expose screen UI state
private val _uiState = MutableStateFlow(DiceUiState())
val uiState: StateFlow<DiceUiState> = _uiState.asStateFlow()
// Handle business logic
fun rollDice() {
_uiState.update { currentState ->
currentState.copy(
firstDieValue = Random.nextInt(from = 1, until = 7),
secondDieValue = Random.nextInt(from = 1, until = 7),
numberOfRolls = currentState.numberOfRolls + 1,
)
}
}
}
Puoi quindi accedere a ViewModel da un elemento componibile a livello di schermata come segue:
import androidx.lifecycle.viewmodel.compose.viewModel
// Use the 'viewModel()' function from the lifecycle-viewmodel-compose artifact
@Composable
fun DiceRollScreen(
viewModel: DiceRollViewModel = viewModel()
) {
val uiState by viewModel.uiState.collectAsStateWithLifecycle()
// Update UI elements
}
Utilizzare le coroutine con ViewModel
ViewModel include il supporto per le coroutine Kotlin. È in grado di mantenere il lavoro asincrono nello stesso modo in cui mantiene lo stato dell'UI.
Per ulteriori informazioni, consulta Utilizzare le coroutine Kotlin con i componenti dell'architettura Android.
Il ciclo di vita di un ViewModel
Il ciclo di vita di un ViewModel è legato direttamente al suo ambito. Un ViewModel
rimane in memoria finché non scompare il ViewModelStoreOwner a cui è definito l'ambito
Questo può verificarsi nei seguenti contesti:
- Nel caso di un'attività, quando termina.
- Nel caso di una voce di navigazione, quando viene rimossa dallo stack indietro.
- Nel caso di un elemento componibile, quando esce dalla composizione.
Puoi utilizzare
rememberViewModelStoreOwnerper definire l'ambito di un ViewModel direttamente in una parte arbitraria dell'UI (ad esempio unPagero unLazyList).
In questo modo, ViewModel è una soluzione ideale per archiviare i dati che sopravvivono alle modifiche alla configurazione.
La Figura 1 illustra i vari stati del ciclo di vita di un'attività durante una rotazione e al termine. L'illustrazione mostra anche la durata di
ViewModel accanto al ciclo di vita dell'attività associata. Questo diagramma specifico illustra gli stati di un'attività.
In genere, richiedi un ViewModel la prima volta che il sistema chiama il metodo onCreate() di un oggetto attività. Il sistema può chiamare
onCreate() più volte durante l'esistenza di un'attività, ad esempio quando lo schermo di un dispositivo viene ruotato. Il ViewModel esiste dal momento in cui
richiedi per la prima volta un ViewModel fino al termine e all'eliminazione dell'attività.
Cancellare le dipendenze di ViewModel
ViewModel chiama il onCleared metodo quando il ViewModelStoreOwner
lo elimina nel corso del suo ciclo di vita. In questo modo puoi liberare spazio da qualsiasi lavoro o dipendenza che segue il ciclo di vita di ViewModel.
L'esempio seguente mostra un'alternativa a viewModelScope.
viewModelScope è un CoroutineScope integrato che
segue automaticamente il ciclo di vita di ViewModel. ViewModel lo utilizza per attivare operazioni correlate all'attività. Se vuoi utilizzare un ambito personalizzato invece
di viewModelScope per semplificare i test, ViewModel può ricevere un
CoroutineScope come dipendenza nel suo costruttore. Quando ViewModelStoreOwner cancella ViewModel alla fine del suo ciclo di vita, ViewModel annulla anche CoroutineScope.
class MyViewModel(
private val coroutineScope: CoroutineScope =
CoroutineScope(SupervisorJob() + Dispatchers.Main.immediate)
) : ViewModel() {
// Other ViewModel logic ...
override fun onCleared() {
coroutineScope.cancel()
}
}
A partire dalla versione 2.5 del ciclo di vita,
puoi passare uno o più oggetti Closeable
al costruttore di ViewModel che si chiudono automaticamente quando l'istanza di
ViewModel viene cancellata.
class CloseableCoroutineScope(
context: CoroutineContext = SupervisorJob() + Dispatchers.Main.immediate
) : Closeable, CoroutineScope {
override val coroutineContext: CoroutineContext = context
override fun close() {
coroutineContext.cancel()
}
}
class MyViewModel(
private val coroutineScope: CoroutineScope = CloseableCoroutineScope()
) : ViewModel(coroutineScope) {
// Other ViewModel logic ...
}
Best practice
Di seguito sono riportate alcune best practice chiave da seguire quando implementi ViewModel:
- A causa del loro ambito, utilizza ViewModel come dettagli di implementazione di un holder di stato a livello di schermata. Non utilizzarli come holder di stato di componenti UI riutilizzabili come gruppi di chip o moduli. In caso contrario, otterresti la stessa istanza di ViewModel in diversi utilizzi dello stesso componente UI nello stesso ViewModelStoreOwner, a meno che non utilizzi una chiave di visualizzazione esplicita per chip.
- ViewModel non deve conoscere i dettagli di implementazione dell'UI. Mantieni i nomi dei metodi esposti dall'API ViewModel e quelli dei campi dello stato dell'UI il più generici possibile. In questo modo, ViewModel può ospitare qualsiasi tipo di UI: uno smartphone, un dispositivo pieghevole, un tablet o persino un Chromebook.
- Poiché possono potenzialmente vivere più a lungo di
ViewModelStoreOwner, ViewModel non deve contenere riferimenti ad API correlate al ciclo di vita comeContextoResourcesper evitare perdite di memoria. - Non passare ViewModel ad altre classi, funzioni o altri componenti UI. Poiché la piattaforma li gestisce, devi mantenerli il più vicino possibile: vicino all'attività, alla funzione componibile a livello di schermata o alla destinazione di navigazione. In questo modo, i componenti di livello inferiore non accedono a più dati e logica di quelli necessari.
Ulteriori informazioni
Man mano che i dati diventano più complessi, potresti scegliere di avere una classe separata solo per caricare i dati. Lo scopo di ViewModel è incapsulare i dati per
un controller UI in modo che i dati sopravvivano alle modifiche alla configurazione. Per informazioni
su come caricare, mantenere e gestire i dati durante le modifiche alla configurazione, consulta
Stati dell'UI salvati.
La Guida all'architettura delle app Android suggerisce di creare una classe repository per gestire queste funzioni.
Risorse aggiuntive
Per ulteriori informazioni sulla classe ViewModel, consulta le seguenti risorse.
Documentazione
Contenuti delle visualizzazioni
Esempi
Consigliati per te
- Nota: il testo del link viene visualizzato quando JavaScript è disattivato
- Utilizzare le coroutine Kotlin con i componenti che riconoscono il ciclo di vita
- Salvare gli stati dell'UI
- Caricare e visualizzare i dati impaginati