RoomDB <- -> Repository <- -> ViewModel 로 연결되는 구조를 생성하기 위해 뷰모델을 생성하다가 오류가 발생하였다.
private lateinit var hospitalViewModel : HospitalViewModel
override fun initView() {
binding.lifecycleOwner = this
// Error
hospitalViewModel = ViewModelProvider(this).get(HospitalViewModel::class.java)
....
}
Cannot create an instance of class
HospitalViewModel의 인스턴스 클래스를 생성할 수 없다고 한다.
소스를 들여다보니
ViewModelProvider$AndroidViewModelFactory.create()를 수행하는 과정에서
@Suppress("DocumentExceptions")
private fun <T : ViewModel> create(modelClass: Class<T>, app: Application): T {
return if (AndroidViewModel::class.java.isAssignableFrom(modelClass)) {
try {
modelClass.getConstructor(Application::class.java).newInstance(app)
} catch (e: NoSuchMethodException) {
......
}
} else super.create(modelClass)
}
isAssignableFrom : 특정 Class가 어떤 클래스/인터페이스를 상속/구현했는지 체크합니다.
나의 modelClass가 AndroidViewModel을 상속받지 않음을 확인하였다.
AndroidViewModel::class.java.isAssignableFrom(modelClass)
class HospitalViewModel(application: Application) : ViewModel() {
....
}
내 뷰모델은 ViewModel을 상속받고 있는데 왜 안될까?? 해서 알아보니
AndroidViewModel과 ViewModel은 서로 다른점이 있었다.
View 모델 내에서 컨텍스트를 사용해야 하는 경우 응용프로그램 컨텍스트가 포함되어 있으므로 AVM(Android View Model)을 사용해야 합니다.
Android View Model에는 Application Context를 가지고 있다. 그러기에 정적(Static)한 컨텍스트 인스턴스가 있으면 메모리 누수가 발생할 수 있기에 조심해야한다.
정적 어플리케이션 인스턴트를 앱내에서 하나만 사용해야 한다면, 나쁘지 않은 작업이다.
특정 클래스에서의 Application 인스턴트를 사용하는 것은 문제가 되지않으나, 이를 참조한다면 참조 주기의 문제가 생긴다.
즉 Context관련 작업을 수행하려면 Android ViewModel을 사용하되, Default값은 Viewmodel을 사용하자. 정도로 이해하면 될 것 같다.
AndroidViewModel StackoverFlow
나는 RoomDB에 접근해야만 하고 RoomDB는 싱글톤 인스턴트로 생성되어 있기 때문에 AndroidViewModel을 상속받아 사용하여 문제를 해결하였다.
class HospitalViewModel(application: Application) : AndroidViewModel(application) {
....
}