ViewModel이 생성 및 저장되는 과정

홍성덕·2024년 8월 13일

Android ViewModel

목록 보기
2/4
// ImageDetailActivity.kt
private val viewModel = ViewModelProvider(this@ImageDetailActivity).get(ImageDetailViewModel::class.java)

이 코드를 파헤쳐보면서 어떻게 ViewModel이 생성되는지 알아보겠다. 먼저 ViewModelProvider(this@ImageDetailActivity) 이 코드를 통해서 ViewModelProvider 객체를 생성한다.
ViewModelProvider 부분에서 Cmd + Option + B를 누르면

    public constructor(
        owner: ViewModelStoreOwner
    ) : this(owner.viewModelStore, defaultFactory(owner), defaultCreationExtras(owner))

위와 같은 secondary constructor 선언부로 연결된다. ImageDetailActivity의 viewModelStore과 defaultFactory, defaultCreationExtras를 통해 ViewModelProvider 객체를 생성한다는 것을 알 수 있다. 참고로 Factory와 CreationExtras는 구현 및 상속하여 default가 아닌 개발자가 직접 만든 클래스의 객체를 사용하는 것도 가능하다. 하지만 이 글에서는 다루지 않겠다.

그러면 이제 get(ImageDetailViewModel::class.java) 코드의 get() 메서드를 따라가보자.

    @MainThread
    public open operator fun <T : ViewModel> get(modelClass: Class<T>): T {
        val canonicalName = modelClass.canonicalName
            ?: throw IllegalArgumentException("Local and anonymous classes can not be ViewModels")
        return get("$DEFAULT_KEY:$canonicalName", modelClass)
    }

여기서 다시 한번 return되는 get("$DEFAULT_KEY:$canonicalName", modelClass) 메서드 선언부로 가보자.

    @Suppress("UNCHECKED_CAST")
    @MainThread
    public open operator fun <T : ViewModel> get(key: String, modelClass: Class<T>): T {
        val viewModel = store[key]
        if (modelClass.isInstance(viewModel)) {
            (factory as? OnRequeryFactory)?.onRequery(viewModel!!)
            return viewModel as T
        } else {
            @Suppress("ControlFlowWithEmptyBody")
            if (viewModel != null) {
                // TODO: log a warning.
            }
        }
        val extras = MutableCreationExtras(defaultCreationExtras)
        extras[VIEW_MODEL_KEY] = key
        // AGP has some desugaring issues associated with compileOnly dependencies so we need to
        // fall back to the other create method to keep from crashing.
        return try {
            factory.create(modelClass, extras)
        } catch (e: AbstractMethodError) {
            factory.create(modelClass)
        }.also { store.put(key, it) }
    }

val viewModel = store[key]를 보면 주어진 key를 통해 ViewModelStore에 저장되어 있던 ViewModel 객체를 가져오고 modelClass.isInstance(viewModel) 조건문을 통해 해당 ViewModel 객체가 modelClass와 호환되는지 확인한다. 조건문이 true이면 기존에 저장되어 있던 ViewModel 객체를 리턴한다.
예시에서 modelClass에 해당하는 인자는 ImageDetailViewModel::class.java이다.

만약 조건문이 true가 아니라면 기존에 저장되어 있는 ViewModel 객체가 없거나(null) modelClass와 호환되는 ViewModel 객체가 없는 것이다. 그래서 factory.create() 메서드를 호출하여 새로운 ViewModel 객체를 생성하고 추가적으로 store.put(key, it)을 통해 ViewModelStore에 해당 ViewModel을 K,V 형태로 저장한다.

factory는 개발자가 직접 선언한 Factory를 사용한 게 아니므로, 아까 ViewModelFactory secondary constructor 선언부에서 봤듯이 defaultFactory(owner)를 사용할 것이다.

	internal fun defaultFactory(owner: ViewModelStoreOwner): Factory =
		if (owner is HasDefaultViewModelProviderFactory)
			owner.defaultViewModelProviderFactory else instance

owner가 HasDefaultViewModelProviderFactory 타입인지 확인하고 조건이 true이면 해당 owner의 defaultViewModelProviderFactory를 리턴한다. HasDefaultViewModelProviderFactory는 인터페이스이므로 이 조건문은 owner가 이 인터페이스를 구현하는 클래스의 객체인지 확인하는 것이다.

이 우리는 액티비티를 owner로 전달하였는데, ComponentActivity 클래스가 HasDefaultViewModelProviderFactory의 구현 클래스이므로, ComponentActivity 클래스의 defaultViewModelProviderFactory를 보면 된다.

// ComponentActivity.kt
    override val defaultViewModelProviderFactory: ViewModelProvider.Factory by lazy {
        SavedStateViewModelFactory(
            application,
            this,
            if (intent != null) intent.extras else null
        )
    }

SavedStateViewModelFactory 객체를 리턴하고 있다. SavedStateViewModelFactory로 가보자.

// SavedStateViewModelFactory.kt
    override fun <T : ViewModel> create(modelClass: Class<T>, extras: CreationExtras): T { ... }

그리고 여기서 그토록 찾던 create() 메서드를 발견할 수 있다. ViewModel 타입으로 제한된 T를 리턴하므로 이 메서드가 ViewModel 객체를 리턴하는 것을 알 수 있다. 아까 위에서 봤던 get() 메서드에서 호출하는 factory.create()의 선언부가 바로 여기인 것이다.

나는 액티비티를 예시로 코드를 살펴봤지만 프래그먼트에서도 똑같이 이런 방식으로 코드를 살펴볼 수 있다.


그럼 마지막으로 그림을 보면서 ViewModel 객체를 생성하는 과정을 정리해보겠다.

  1. 먼저 ViewModelProvider 객체에 ViewModel 인스턴스를 요청한다.
  2. ViewModelProvider 객체를 생성할 때 ViewModelStoreOwner 인터페이스를 구현하는 클래스의 객체를 전달한다.
  3. ViewModelStoreOwner의 ViewModelStore에 이미 만들어져 있는 ViewModel 객체가 있는지 확인하고 있으면 store에서 get() 메서드로 꺼내서 요청받은 ViewModel 객체를 리턴한다.
  4. store에 없다면 ViewModelProvider.Factory 인터페이스를 구현하는 클래스의 객체를 통해서 create()메서드를 호출하여 ViewModel 객체를 생성 및 리턴하며, 추가적으로 store에 저장한다.

참고자료

profile
안드로이드 주니어 개발자

0개의 댓글