ViewModel 作用域 API Android Jetpack 的一部分。
作用域是有效使用 ViewModel 的关键。每个 ViewModel 的作用域都限定为一个实现 ViewModelStoreOwner 接口的对象。有多个 API 可帮助您更轻松地管理 ViewModel 的作用域。本文档简要介绍了您应该了解的一些关键技术。
借助 ViewModelProvider.get() 方法,您可以获取作用域限定为任何 ViewModelStoreOwner 的 ViewModel 实例。对于 Kotlin 用户,我们针对最常见的用例提供了不同的扩展函数。所有 Kotlin 扩展函数实现从本质上讲都会使用 ViewModelProvider API。
ViewModel 的作用域限定为最近的 ViewModelStoreOwner
您可以将 ViewModel 的作用域限定为可组合函数、Activity 或导航图的目的地。借助 Compose 中的 viewModel() 函数,您可以获取作用域限定为最近的 ViewModelStoreOwner 的 ViewModel 实例。
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 的作用域限定为任何 ViewModelStoreOwner
viewModel() 函数接受可选的 viewModelStoreOwner 参数,可用于指定 ViewModel 实例的作用域限定为哪个 ViewModelStoreOwner。
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 的作用域限定为可组合项
您可以使用 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
}
}
}
ViewModel 的作用域限定为 Navigation 图
Navigation 图也是 ViewModel Store Owner。如果您使用的是 Navigation Compose,可以使用 getBackStackEntry() 函数获取作用域限定为某个 Navigation 图的 ViewModel 实例。
viewModel() 从 LocalViewModelStoreOwner CompositionLocal 提供的最近 ViewModelStoreOwner 中检索实例。在使用 Jetpack Navigation 的典型 Compose 应用中,此所有者是当前的导航返回堆栈条目。这意味着,只要相应目的地存在于返回堆栈中,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) // ... } }
如果除了 Jetpack Navigation 之外,您还使用 Hilt,则可以使用 hiltNavGraphViewModels(graphId) API,如下所示。
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 和作用域,请参阅下面列出的其他资源: