APIs de escopo do ViewModel Parte do Android Jetpack.
A definição do escopo é fundamental para usar os ViewModels de maneira eficaz. Cada ViewModel tem um
objeto que implementa a interface ViewModelStoreOwner como escopo. Existem várias
APIs que permitem gerenciar o escopo dos ViewModels com mais facilidade.
Neste documento, descrevemos algumas das principais técnicas que você precisa conhecer.
O método ViewModelProvider.get() permite acessar uma instância de um ViewModel
com qualquer ViewModelStoreOwner como escopo. Para usuários do Kotlin, existem diferentes
funções de extensão disponíveis para os casos de uso mais comuns. Todas as implementações
de funções de extensão do Kotlin usam a API ViewModelProvider internamente.
ViewModels com escopo para o ViewModelStoreOwner mais próximo
Você pode definir um elemento combinável, uma atividade ou um destino de um gráfico de navegação como escopo de um ViewModel. A função viewModel() no Compose permite acessar uma instância do ViewModel que tem como escopo o ViewModelStoreOwner mais próximo.
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() ) { /* ... */ }
ViewModels com escopo para qualquer ViewModelStoreOwner
A função viewModel() usa um parâmetro viewModelStoreOwner opcional que pode ser usado para especificar a qual ViewModelStoreOwner vai o escopo da instância do 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) ) { /* ... */ }
ViewModels com escopo para um elemento combinável
Você pode usar rememberViewModelStoreOwner() para definir o escopo de um ViewModel diretamente no local da chamada de um elemento combinável. Isso é especialmente útil para componentes de interface que são adicionados ou removidos dinamicamente da tela com base no estado, como os itens de uma página ou lista preguiçosa. Quando o elemento combinável que possui o
ViewModelStoreOwner sai da composição, o ViewModelStore
associado é limpo e o ViewModel é destruído.
Use rememberViewModelStoreOwner() para criar um repositório com reconhecimento de ciclo de vida que permaneça vigente a mudanças de configuração.
@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
}
}
Para implementações mais complexas, como um HorizontalPager ou casos que exigem vários escopos independentes, use rememberViewModelStoreProvider().
Isso permite gerar instâncias ViewModelStoreOwner distintas para diferentes chaves (como índices de página). Dessa forma, cada página mantém seu próprio estado de ViewModel independente.
@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
}
}
}
ViewModels com escopo para o gráfico de navegação
Os gráficos de navegação também são proprietários de armazenamento do ViewModel. Se você estiver usando
Navigation Compose, pode acessar uma instância de um
ViewModel com escopo para um gráfico de navegação com a função
getBackStackEntry().
viewModel() recupera a instância do ViewModelStoreOwner
fornecido pelo LocalViewModelStoreOwner CompositionLocal. Em um aplicativo Compose típico que usa o Jetpack Navigation, esse proprietário é a entrada atual da backstack de navegação. Isso significa que o ViewModel permanece na memória enquanto esse destino estiver presente no backstack.
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) // ... } }
Caso esteja usando o Hilt, além do Jetpack Navigation, é possível usar a
hiltNavGraphViewModels(graphId)
API desta forma:
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) // ... } }
Outros recursos
Para saber mais sobre ViewModels e escopo, consulte os seguintes recursos adicionais: