ViewModel Scoping API Android Jetpack의 구성요소
범위는 ViewModel을 효과적으로 사용하기 위한 핵심입니다. 각 ViewModel의 범위는 ViewModelStoreOwner 인터페이스를 구현하는 객체로 지정됩니다. 몇 가지 API를 사용하면 ViewModel의 범위를 더 쉽게 관리할 수 있습니다.
이 문서에서는 알아야 할 주요 기법을 간략히 설명합니다.
ViewModelProvider.get() 메서드를 사용하면 ViewModelStoreOwner로 범위가 지정된 ViewModel의 인스턴스를 가져올 수 있습니다. Kotlin 사용자의 경우 가장 일반적인 사용 사례에 다양한 확장 함수를 사용할 수 있습니다. 모든 Kotlin 확장 함수 구현은 내부적으로 ViewModelProvider API를 사용합니다.
가장 가까운 ViewModelStoreOwner로 ViewModel 범위 지정
컴포저블, 활동 또는 탐색 그래프의 대상으로 ViewModel의 범위를 지정할 수 있습니다. 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() ) { /* ... */ }
ViewModelStoreOwner로 범위가 지정된 ViewModel
viewModel() 함수는 ViewModel 인스턴스의 범위로 지정할 ViewModelStoreOwner를 지정하는 데 사용할 수 있는 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
}
}
}
탐색 그래프로 범위가 지정된 ViewModel
탐색 그래프도 ViewModel 스토어 소유자입니다. Navigation Compose를 사용하는 경우 getBackStackEntry() 함수를 사용하여 탐색 그래프로 범위가 지정된 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 및 범위에 관해 자세히 알아보려면 다음 추가 리소스를 참고하세요.