API для определения области видимости ViewModel — часть Android Jetpack .

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

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

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

Вы можете ограничить область действия ViewModel до компонуемого объекта , Activity или целевого объекта в навигационном графе. Функция 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)
) {
    /* ... */
}

ViewModels ограничены областью действия составного элемента

Вы можете использовать rememberViewModelStoreOwner() для ограничения области видимости ViewModel непосредственно местом вызова составного объекта. Это особенно полезно для компонентов пользовательского интерфейса, которые динамически добавляются или удаляются с экрана в зависимости от состояния, например, элементы страницы или ленивый список. Когда составной объект, которому принадлежит 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, ограниченные областью действия графа навигации.

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

viewModel() получает экземпляр из ближайшего ViewModelStoreOwner предоставленного классом LocalViewModelStoreOwner CompositionLocal . В типичном приложении Compose, использующем Jetpack Navigation, этим владельцем является текущая запись в стеке возврата 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 и области видимости, см. следующие дополнительные ресурсы:

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

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