ViewModel Scoping API   Входит в Android Jetpack.

Область видимости – это ключ к эффективному использованию ViewModel. Каждая модель ViewModel привязана к объекту, который реализует интерфейс ViewModelStoreOwner. Существует несколько API, которые позволяют вам более легко управлять областью действия ваших ViewModels. В этом документе описаны некоторые из основных методов, о которых вам следует знать.

Метод ViewModelProvider.get() позволяет получить экземпляр ViewModel, ограниченный любым ViewModelStoreOwner. Для пользователей Kotlin доступны различные функции расширения, которые можно применять в наиболее распространенных случаях. Все реализации функций расширения Kotlin используют ViewModelProvider API.

ViewModel, ограниченные ближайшим ViewModelStoreOwner

Вы можете ограничить область действия ViewModel composable-функцией, действием или пунктом назначения графа навигации. Функция viewModel() в Compose позволяет получить экземпляр ViewModel, ограниченный ближайшим ViewModelStoreOwner.

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, ограниченные любым ViewModelStoreOwner

Функция viewModel() принимает необязательный параметр viewModelStoreOwner, с помощью которого можно указать, к какому ViewModelStoreOwner относится экземпляр 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 в области видимости composable-функции

Вы можете использовать rememberViewModelStoreOwner(), чтобы ограничить область действия ViewModel непосредственно местом вызова composable-функции. Это особенно полезно для компонентов пользовательского интерфейса, которые динамически добавляются на экран или удаляются с него в зависимости от состояния, например для элементов страницы или ленивого списка. Когда composable-функция, которой принадлежит ViewModelStoreOwner, покидает композицию, связанный с ней объект ViewModelStore очищается, а ViewModel удаляется.

Используйте rememberViewModelStoreOwner(), чтобы создать хранилище, учитывающее жизненный цикл и сохраняющее данные при изменении конфигурации.

@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
    }
}

Для более сложных реализаций, например HorizontalPager или случаев, когда требуется несколько независимых областей действия, используйте rememberViewModelStoreProvider(). Это позволяет создавать отдельные экземпляры ViewModelStoreOwner для разных ключей (например, индексов страниц). Таким образом, каждая страница поддерживает свое собственное независимое состояние ViewModel.

@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 scoped to the Navigation graph

Графы навигации также являются владельцами хранилища ViewModel. Если вы используете Navigation Compose, то можете получить экземпляр ViewModel, ограниченный навигационным графом, с помощью функции getBackStackEntry().

viewModel() извлекает экземпляр из ближайшего ViewModelStoreOwner, предоставленного LocalViewModelStoreOwner CompositionLocal. В типичном приложении Compose, использующем Jetpack Navigation, этим владельцем является текущая запись обратного стека навигации. Это означает, что ViewModel остается в памяти до тех пор, пока этот пункт назначения присутствует в обратном стеке.

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)
        // ...
    }
}

Если вы используете Hilt в дополнение к Jetpack Navigation, вы можете использовать API hiltNavGraphViewModels(graphId), как показано ниже.

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)
        // ...
    }
}

Дополнительные ресурсы

Чтобы узнать больше о ViewModel и области видимости, ознакомьтесь со следующими дополнительными ресурсами:

Документация

Просмотр контента