ViewModel 範圍設定 API Android Jetpack 的一項工具
範圍是有效使用 ViewModel 的關鍵。每個 ViewModel 的範圍都是限定為實作 ViewModelStoreOwner 介面的物件。您可以運用幾種 API 更輕鬆管理 ViewModel 的範圍。本文件將概述您應瞭解的一些主要技巧。
ViewModelProvider.get() 方法可讓您取得範圍限定為任何 ViewModelStoreOwner 的 ViewModel 執行個體。如果是 Kotlin 使用者,針對常見的用途提供不同的擴充功能。所有 Kotlin 擴充功能函式實作都會使用 ViewModelProvider API 進階功能。
將 ViewModel 範圍限定為最接近的 ViewModelStoreOwner
您可以將 ViewModel 的範圍限定為可組合函式、活動或 Navigation 圖表的目的地。Compose 中的 viewModel() 函式可讓您取得 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() ) { /* ... */ }
範圍限定為任何 ViewModelStoreOwner 的 ViewModel
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 範圍限定在可組合函式的呼叫位置。如果 UI 元件會根據狀態動態新增至畫面或從畫面中移除,這項功能就特別實用,例如頁面或延遲清單的項目。當擁有 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
}
}
}
範圍限定為 Navigation 圖形的 ViewModel
Navigation 圖表也是 ViewModel 儲存區擁有者。使用 Navigation Compose 時,您可以透過 getBackStackEntry() 函式,取得 ViewModel 範圍限定為 Navigation 圖表的執行個體。
viewModel() 會從 LocalViewModelStoreOwner CompositionLocal 提供的最接近 ViewModelStoreOwner 擷取例項。在一般使用 Jetpack Navigation 的 Compose 應用程式中,這個擁有者是目前的 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) // ... } }
除了 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 和範圍,請參閱下列其他資源: