java.lang.RuntimeException: Cannot create an instance of class com.example ViewModel 오류

Heejin Ryu·2021년 7월 16일
1

잘못된 ProfileViewModel 의 생성

DI를 사용하지 않은 상태에서 Parameter가 있는 뷰모델을 생성하고자 하는데, fragment내에서 생성해주는 코드가 잘못됨.

Fragment에서 아래와 같이 프로필뷰모델을 생성해주었다.

private val profileViewModel by activityViewModels<ProfileViewModel>()

뷰모델에서는 아래와 같이 Repository를 parmeter로 받도록 정의해주었다.

class ProfileViewModel(
    private val userRepository: UserRepository,
) : ViewModel() {

		/// blah blah
}

DI를 사용하지 않을 때에는 생성 방법이 조금 다른데, ViewModelProvider.Factory를 사용하여 생성해주어야한다고 한다.

기본적으로 파라미터가 없는 ViewModel 경우에서 파라미터가 있는 ViewModel까지 코드를 간단하게 알아보자.

직접 ViewModelProvider.Factory 인터페이스를 구현하면 되는데, 이 방법의 장점은 하나의 팩토리로 다양한 ViewModel 클래스를 관리할 수 있는 것이라고 한다. 그리고 따로 코드를 구현하기 때문에, 다양한 상황에 대해서 커스텀하게 컨트롤이 가능하다고 한다.

Parameter가 없는 ViewModel with ViewModelProvider.Factory

class ProfileViewModelFactory : ViewModelProvider.Factory {
    override fun <T : ViewModel?> create(modelClass: Class<T>): T {
        return if (modelClass.isAssignableFrom(ProfileViewModel::class.java)) {
            ProfileViewModel() as T
        } else {
            throw IllegalArgumentException()
        }
    }
}

위 코드는 ProfileViewModel 클래스가 아니면 IllegalArgumentEception을 던지도록 구현한 것이라고 한다. 이렇게 개발자 나름대로 어떤 상황을 위한 코드를 구현할 수 있다.

Parameter가 있는 ViewModel with ViewModelProvider.Factory

위의 코드를 조금 더 고쳐서 파라메터가 있는 ViewModel의 Instance도 구현할 수가 있다. ProfileViewModel을 예시로 사용해보려고 한다.

ViewModel

class ProfileViewModel(
    private val userRepository: UserRepository,
) : ViewModel() {

		/// blah blah
}

ViewModel은 내가 생성해놓은 ViewModel을 그대로 가져온다.

ViewModelFactory

class ProfileViewModelFactory(userRepository: UserRepository) : ViewModelProvider.Factory {
    override fun <T : ViewModel?> create(modelClass: Class<T>): T {
        return if (modelClass.isAssignableFrom(ProfileViewModel::class.java)) {
            ProfileViewModel(userRepository) as T
        } else {
            throw IllegalArgumentException()
        }
    }
}

ViewModelFactory의 경우 위에서 정의한 Factory 코드에서 ProfileViewMdoel을 생성할 때 내부에 UserRepository()를 생성해서 넣어주었다.

Activity/ Fragment

private lateinit var profileViewModel: ProfileViewModel

override fun onCreate(savedInstanceState: Bundle?) {
		super.onCreate(savedInstanceState)
		setContentView(R.layout.activity_main0

		profileViewModel = ViewModelProvider(this, ProfileViewModelFactory(UserRepository()))
				.get(ProfileViewModel::class.java)
}

내가 바꾼 코드

ViewModel은 그대로 두고, Fragment에 생성한 코드만 아래와 같이 바꿨다. 굳이 ViewModelProvider interface를 따로 구현하지 않고 object키워드를 사용해서 내부에 간단하게 선언해주었다.

private val profileViewModel by viewModels<ProfileViewModel> {
        object: ViewModelProvider.Factory {
            override fun <T : ViewModel?> create(modelClass: Class<T>): T {
                return ProfileViewModel(UserRepository(requireContext())) as T
            }
        }
    }

Reference

https://readystory.tistory.com/176

profile
Chocolate lover🍫 & Junior Android developer🤖

0개의 댓글