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 和作用域,请参阅下面列出的其他资源:

文档

查看内容