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 클래스를 관리할 수 있는 것이라고 한다. 그리고 따로 코드를 구현하기 때문에, 다양한 상황에 대해서 커스텀하게 컨트롤이 가능하다고 한다.
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을 던지도록 구현한 것이라고 한다. 이렇게 개발자 나름대로 어떤 상황을 위한 코드를 구현할 수 있다.
위의 코드를 조금 더 고쳐서 파라메터가 있는 ViewModel의 Instance도 구현할 수가 있다. ProfileViewModel을 예시로 사용해보려고 한다.
class ProfileViewModel(
private val userRepository: UserRepository,
) : ViewModel() {
/// blah blah
}
ViewModel은 내가 생성해놓은 ViewModel을 그대로 가져온다.
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()를 생성해서 넣어주었다.
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
}
}
}