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:

Documentação

Conteúdo de visualizações