[Android, Kotlin] Cannot create an instance of class ViewModel 문제 해결하기

 

[문제 상황]

 

Caused by: java.lang.RuntimeException: Cannot create an instance of class ...ViewModel

 

위와 같은 에러 코드가 발생했다.

 

에러는 ViewModel에 파라미터를 객체를 넘겨주는 코드에서 발생했다.

 

class RoutineViewModel(
    mainActivity: MainActivity
): ViewModel(){

 

위와 같이 routine Model을 관리하는 RoutineViewModel에서 mainActivity를 인자로 받아야 했기에 

RoutineViewModel 클래스의 생성자에서 mainActivity를 파라미터로 받기 위한 동작이 필요했다.

 

 

[문제 해결]

 

문제 해결을 위해서  ViewModelProvider를 살펴봤다.

ViewModelProvider는 ViewModel 객체를 생성하기 위해서 사용하는 클래스이다.

Activity나 Fragment에서 ViewModelProvider를 통해서 자기 자신을 생성자로 전달하여 ViewModel 인스턴스를 얻는다.

 

ViewModelProvider의 내부 코드를 살펴보면,

public open class ViewModelProvider
/**
 * Creates a ViewModelProvider
 *
 * @param store `ViewModelStore` where ViewModels will be stored.
 * @param factory factory a `Factory` which will be used to instantiate new `ViewModels`
 * @param defaultCreationExtras extras to pass to a factory
 */
@JvmOverloads
constructor(
    private val store: ViewModelStore,
    private val factory: Factory,
    private val defaultCreationExtras: CreationExtras = CreationExtras.Empty,
)

 

ViewModel을 인스턴스화 하기 위해서 Factory 객체를 사용한다.

 

public interface Factory {
    /**
     * Creates a new instance of the given `Class`.
     *
     * Default implementation throws [UnsupportedOperationException].
     *
     * @param modelClass a `Class` whose instance is requested
     * @return a newly created ViewModel
     */
    public fun <T : ViewModel> create(modelClass: Class<T>): T {
        throw UnsupportedOperationException(
            "Factory.create(String) is unsupported.  This Factory requires " +
                "`CreationExtras` to be passed into `create` method."
        )
    }

    /**
     * Creates a new instance of the given `Class`.
     *
     * @param modelClass a `Class` whose instance is requested
     * @param extras an additional information for this creation request
     * @return a newly created ViewModel
     */
    public fun <T : ViewModel> create(modelClass: Class<T>, extras: CreationExtras): T =
        create(modelClass)

    companion object {
        /**
         * Creates an [InitializerViewModelFactory] using the given initializers.
         *
         * @param initializers the class initializer pairs used for the factory to create
         * simple view models
         */
        @JvmStatic
        fun from(vararg initializers: ViewModelInitializer<*>): Factory =
            InitializerViewModelFactory(*initializers)
    }
}

 

Factory 객체의 내부를 살펴보면, 원하는 인자를 전달해서 새로 생성된

ViewModel 인스턴스를 반환하는 구조를 가지고 있다.

 

따라서 ViewModel에 인자를 전달하기 위해서는 해당 인자를 포함하는 

Factory 객체를 생성해서 ViewModelProvider에 같이 전달하는 구조로 만들어야 한다.

 

이를 해결하기 위해서 Factory 객체를 ViewModel 클래스 안에 정의했다.

 

//View Model Class 선언

class RoutineViewModel(
    mainActivity: MainActivity
): ViewModel(){
    class Factory(val mainActivity: MainActivity) : ViewModelProvider.Factory{
        override fun <T : ViewModel> create(modelClass: Class<T>): T {
            return RoutineViewModel(mainActivity) as T
        }
    }

 

이제 mainActivity에서 ViewModel을 정의할 때 ViewModelProvider에 선언한 Factory 객체를 인자로 전달한다.

 

//View Layer -> ViewModel을 선언하는 곳

val routineViewModel by lazy {
    ViewModelProvider(this@MainActivity,RoutineViewModel
        .Factory(this@MainActivity))[RoutineViewModel::class.java]
}

 

결과로 ViewModel에서는 전달 받은 인자를 사용할 수 있게 되었다.