ViewModel Scoping APIs Một phần của Android Jetpack.
Phạm vi là yếu tố then chốt để sử dụng ViewModel một cách hiệu quả. Mỗi ViewModel sẽ thuộc một phạm vi đối tượng triển khai giao diện ViewModelStoreOwner. Có một số API giúp bạn dễ dàng quản lý phạm vi của ViewModel.
Tài liệu này nêu ra một số kỹ thuật chính mà bạn nên biết.
Phương thức ViewModelProvider.get() cho phép bạn có được thực thể của một ViewModel thuộc phạm vi của ViewModelStoreOwner bất kỳ. Người dùng Kotlin có các hàm mở rộng khác nhau dành cho trường hợp sử dụng phổ biến nhất. Về bản chất, tất cả các phương thức triển khai hàm mở rộng Kotlin đều sử dụng API ViewModelProvider.
ViewModel thuộc phạm vi của ViewModelStoreOwner gần nhất
Bạn có thể thiết lập phạm vi của ViewModel trong một thành phần kết hợp, Hoạt động hoặc đích đến của biểu đồ Điều hướng. Hàm viewModel() trong Compose cho phép bạn lấy một thực thể của ViewModel thuộc phạm vi của ViewModelStoreOwner gần nhất.
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 thuộc phạm vi của ViewModelStoreOwner bất kỳ
Hàm viewModel() lấy một tham số viewModelStoreOwner (không bắt buộc) mà bạn có thể dùng để chỉ định ViewModelStoreOwner nào mà thực thể của ViewModel nằm trong đó.
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 thuộc phạm vi của một thành phần kết hợp
Bạn có thể dùng rememberViewModelStoreOwner() để trực tiếp đặt phạm vi cho một ViewModel vào vị trí gọi của một thành phần kết hợp. Điều này đặc biệt hữu ích đối với các thành phần giao diện người dùng được thêm vào hoặc xoá khỏi màn hình một cách linh hoạt dựa trên trạng thái, chẳng hạn như các mục của một trang hoặc danh sách tải từng phần. Khi thành phần kết hợp sở hữu ViewModelStoreOwner rời khỏi thành phần, ViewModelStore được liên kết sẽ bị xoá và ViewModel bị huỷ.
Sử dụng rememberViewModelStoreOwner() để tạo một kho lưu trữ có nhận biết vòng đời và vẫn tồn tại sau khi thay đổi cấu hình.
@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
}
}
Đối với các hoạt động triển khai phức tạp hơn, chẳng hạn như HorizontalPager hoặc các trường hợp yêu cầu nhiều phạm vi độc lập, hãy dùng rememberViewModelStoreProvider().
Điều này cho phép bạn tạo các thực thể ViewModelStoreOwner riêng biệt cho các khoá khác nhau (chẳng hạn như chỉ mục trang). Bằng cách này, mỗi trang sẽ duy trì trạng thái ViewModel độc lập của riêng mình.
@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 thuộc phạm vi của biểu đồ Điều hướng
Biểu đồ điều hướng cũng là chủ sở hữu cửa hàng ViewModel. Nếu đang sử dụng Thành phần điều hướng trong Compose, bạn có thể nhận một thực thể của ViewModel trong phạm vi biểu đồ Điều hướng bằng hàm getBackStackEntry().
viewModel() truy xuất thực thể từ ViewModelStoreOwner gần nhất do LocalViewModelStoreOwner CompositionLocal cung cấp. Trong một ứng dụng Compose điển hình sử dụng thành phần Điều hướng Jetpack, chủ sở hữu này là mục nhập ngăn xếp lui Điều hướng hiện tại. Điều này có nghĩa là ViewModel vẫn nằm trong bộ nhớ miễn là đích đến đó có trong ngăn xếp lui.
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) // ... } }
Nếu đang dùng Hilt cùng với thành phần Điều hướng của Jetpack, bạn có thể sử dụng API hiltNavGraphViewModels(graphId) như sau.
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) // ... } }
Tài nguyên khác
Để tìm hiểu thêm về ViewModel và phạm vi, hãy xem các tài nguyên bổ sung sau đây: