컴포즈에서의 뷰모델 생성

컴포즈에서 뷰모델을 생성 시 크게 두 가지 방법이 사용된다.

viewModel()를 이용한 생성과 hiltViewModel() 를 이용한 생성, 두가지 방법을 자세히 알아보자

사전지식

NavBackStackEntry, ViewModelStoreOwner - NavBackStackEntry와 컴포즈에서의 ViewModel 공유

CompositionLocal → CompositionLocal 참고

1. viewModel()

먼저 viewModel() 함수를 살펴보자

@Suppress("MissingJvmstatic")
@Composable
public inline fun <reified VM : ViewModel> viewModel(
    viewModelStoreOwner: ViewModelStoreOwner = checkNotNull(LocalViewModelStoreOwner.current) {
        "No ViewModelStoreOwner was provided via LocalViewModelStoreOwner"
    },
    key: String? = null,
    factory: ViewModelProvider.Factory? = null,
    extras: CreationExtras = if (viewModelStoreOwner is HasDefaultViewModelProviderFactory) {
        viewModelStoreOwner.defaultViewModelCreationExtras
    } else {
        CreationExtras.Empty
    }
): VM = viewModel(VM::class, viewModelStoreOwner, key, factory, extras)

가장 먼저 viewModelStoreOwner라는 변수에 LocalViewModelStoreOwner.current 로 현재 ViewModelStoreOwner 를 불러옴

일단 결과부터 말하자면 LocalViewModelStoreOwner.current는 현재 컴포즈 뷰에 대해서 최상위 뷰의 ViewModelStoreOwner를 불러온다.

LocalViewModelStoreOwner.current

public object LocalViewModelStoreOwner {
    private val LocalViewModelStoreOwner =
        compositionLocalOf<ViewModelStoreOwner?> { null }

    /**
     * Returns current composition local value for the owner or `null` if one has not
     * been provided nor is one available via [findViewTreeViewModelStoreOwner] on the
     * current [androidx.compose.ui.platform.LocalView].
     */
    public val current: ViewModelStoreOwner?
        @Composable
        get() = LocalViewModelStoreOwner.current ?: findViewTreeViewModelStoreOwner()

    /**
     * Associates a [LocalViewModelStoreOwner] key to a value in a call to
     * [CompositionLocalProvider].
     */
    public infix fun provides(viewModelStoreOwner: ViewModelStoreOwner):
        ProvidedValue<ViewModelStoreOwner?> {
        return LocalViewModelStoreOwner.provides(viewModelStoreOwner)
    }
}

ViewModelStoreOwner.current - ViewModelStoreOwner? 타입의 변수

  1. LocalViewModelStoreOwner 라는 이름의 CompositionLocal 인스턴스에 대해, .current를 호출하여 현재 CompositionLocal 에 provide된 ViewModelStoreOwner를 가져옴

  2. findViewTreeViewModelStoreOwner() 를 통해 LocalView에 제공된 ViewModelStoreOwner를 찾아 리턴한다

    /**
     * The CompositionLocal containing the current Compose [View].
     */
    val LocalView = staticCompositionLocalOf<View> {
        noLocalProvidedFor("LocalView")
    }
    
    
    @JvmName("get")
    public fun View.findViewTreeViewModelStoreOwner(): ViewModelStoreOwner? {
        return generateSequence(this) { view ->
            view.parent as? View
        }.mapNotNull { view ->
            view.getTag(R.id.view_tree_view_model_store_owner) as? ViewModelStoreOwner
        }.firstOrNull()
    }