API di definizione dell'ambito di ViewModel   Parte di Android Jetpack.

L'ambito è fondamentale per utilizzare i ViewModel in modo efficace. Ogni ViewModel è associato a un oggetto che implementa l'interfaccia ViewModelStoreOwner. Esistono diverse API che ti consentono di gestire più facilmente l'ambito dei tuoi ViewModel. Questo documento descrive alcune delle tecniche chiave che devi conoscere.

Il metodo ViewModelProvider.get() consente di ottenere un'istanza di un ViewModel con ambito qualsiasi ViewModelStoreOwner. Per gli utenti Kotlin, sono disponibili diverse funzioni di estensione per i casi d'uso più comuni. Tutte le implementazioni delle funzioni di estensione Kotlin utilizzano l'API ViewModelProvider.

ViewModel con ambito definito sul ViewModelStoreOwner più vicino

Puoi definire l'ambito di un ViewModel in base a un composable, un'attività o una destinazione di un grafico di navigazione. La funzione viewModel() in Compose ti consente di ottenere un'istanza di ViewModel con ambito ViewModelStoreOwner più vicino.

import androidx.lifecycle.viewmodel.compose.viewModel

@Composable
fun MyScreen(
    modifier: Modifier = Modifier,
    // ViewModel API available in lifecycle.lifecycle-viewmodel-compose
    // The ViewModel is scoped to the closest ViewModelStoreOwner provided
    // via the LocalViewModelStoreOwner CompositionLocal. In order of proximity,
    // this could be the destination of a Navigation graph
    // or the host Activity.
    viewModel: MyViewModel = viewModel()
) { /* ... */ }

ViewModel con ambito qualsiasi ViewModelStoreOwner

La funzione viewModel() accetta un parametro viewModelStoreOwner facoltativo che puoi utilizzare per specificare a quale ViewModelStoreOwner è associata l'istanza di ViewModel.

import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.lifecycle.ViewModelStoreOwner

@Composable
fun MyScreen(
    // A custom owner passed in, such as a parent NavBackStackEntry
    customOwner: ViewModelStoreOwner,
    // The ViewModel is now scoped to the provided customOwner
    viewModel: MyViewModel = viewModel(viewModelStoreOwner = customOwner)
) {
    /* ... */
}

ViewModel con ambito definito per un composable

Puoi utilizzare rememberViewModelStoreOwner() per definire l'ambito di un ViewModel direttamente nel sito di chiamata di un composable. Ciò è particolarmente utile per i componenti UI che vengono aggiunti o rimossi dinamicamente dalla schermata in base allo stato, ad esempio gli elementi di una pagina o di un elenco a caricamento lento. Quando il composable che possiede ViewModelStoreOwner esce dalla composizione, ViewModelStore associato viene cancellato e il ViewModel viene eliminato.

Utilizza rememberViewModelStoreOwner() per creare uno store consapevole del ciclo di vita che sopravvive alle modifiche alla configurazione.

@Composable
fun RememberViewModelStoreOwnerSample() {
    // Create a ViewModelStoreOwner scoped to this specific call site.
    // When this composable leaves the composition,
    // the associated ViewModelStore will be cleared.
    val scopedOwner = rememberViewModelStoreOwner()

    CompositionLocalProvider(LocalViewModelStoreOwner provides scopedOwner) {
        // This ViewModel is scoped to `scopedOwner`.
        // It will survive configuration changes but will be cleared when
        // the composable is removed from the UI tree.
        val viewModel = viewModel { TestViewModel("scoped_data") }
        // Use the ViewModel
    }
}

Per implementazioni più complesse, come HorizontalPager o casi che richiedono più ambiti indipendenti, utilizza rememberViewModelStoreProvider(). In questo modo puoi generare istanze ViewModelStoreOwner distinte per chiavi diverse (come gli indici di pagina). In questo modo, ogni pagina mantiene il proprio stato ViewModel indipendente.

@Composable
fun RememberViewModelStoreProviderSample() {
    val storeProvider = rememberViewModelStoreProvider()
    val pages = listOf("Page 1", "Page 2", "Page 3")

    HorizontalPager(pageCount = pages.size) { page ->
        // Create a ViewModelStoreOwner for the specific page using the provider.
        val pageOwner = rememberViewModelStoreOwner(provider = storeProvider, key = page)

        CompositionLocalProvider(LocalViewModelStoreOwner provides pageOwner) {
            val pageViewModel = viewModel { TestViewModel(pages[page]) }
            // Use pageViewModel
        }
    }
}

ViewModel con ambito nel grafico di navigazione

I grafici di navigazione sono anche proprietari dell'archivio ViewModel. Se utilizzi Navigation Compose, puoi ottenere un'istanza di un ViewModel con ambito in un grafico di navigazione con la funzione getBackStackEntry().

viewModel() recupera l'istanza dal ViewModelStoreOwner più vicino fornito da LocalViewModelStoreOwner CompositionLocal. In una tipica applicazione Compose che utilizza Jetpack Navigation, questo proprietario è l'attuale voce dello stack di navigazione. Ciò significa che il ViewModel rimane in memoria finché la destinazione è presente nel back stack.

import androidx.lifecycle.viewmodel.compose.viewModel

@Composable
fun MyAppNavHost() {
    // ...
    composable("myScreen") { backStackEntry ->
        // Retrieve the NavBackStackEntry of "parentNavigationRoute"
        val parentEntry = remember(backStackEntry) {
            navController.getBackStackEntry("parentNavigationRoute")
        }
        // Get the ViewModel scoped to the `parentNavigationRoute` Nav graph
        val parentViewModel: SharedViewModel = viewModel(parentEntry)
        // ...
    }
}

Se utilizzi Hilt oltre a Jetpack Navigation, puoi utilizzare l'API hiltNavGraphViewModels(graphId) come segue.

import androidx.hilt.navigation.compose.hiltViewModel

@Composable
fun MyAppNavHost() {
    // ...
    composable("myScreen") { backStackEntry ->
        val parentEntry = remember(backStackEntry) {
            navController.getBackStackEntry("parentNavigationRoute")
        }

        // ViewModel API available in hilt.hilt-navigation-compose
        // The ViewModel is scoped to the `parentNavigationRoute` Navigation graph
        // and is provided using the Hilt-generated ViewModel factory
        val parentViewModel: SharedViewModel = hiltViewModel(parentEntry)
        // ...
    }
}

Risorse aggiuntive

Per scoprire di più su ViewModel e ambito, consulta le seguenti risorse aggiuntive:

Documentazione

Visualizza contenuti