I have following in my app
Custom Application class:
class MyApplication: Application(){
.....
.....
}
A BaseViewModel with common implementations:
class BaseViewModel(application:MyApplication):AndroidViewModel(application) {
........
}
ViewModel that extends BaseViewModel:
class DataViewModel(application:MyApplication):BaseViewModel(application) {
......
}
I am trying to get ViewModel instance inside fragment
private val viewModel by lazy {
ViewModelProviders.of(this).get(DataViewModel::class.java)
}
But the app crashes with follwoing exception:
Unable to start activity ComponentInfo{com.test/com.test.activity.MainActivity}: java.lang.RuntimeException: Cannot create an instance of class com.test.category.DataViewModel
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2817)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2892)
at android.app.ActivityThread.-wrap11(Unknown Source:0)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1593)
at android.os.Handler.dispatchMessage(Handler.java:105)
at android.os.Looper.loop(Looper.java:164)
at android.app.ActivityThread.main(ActivityThread.java:6541)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:240)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:767)
Caused by: java.lang.RuntimeException: Cannot create an instance of class com.test.category.DataViewModel
at androidx.lifecycle.ViewModelProvider$AndroidViewModelFactory.create(ViewModelProvider.java:232)
at androidx.lifecycle.ViewModelProvider.get(ViewModelProvider.java:164)
at androidx.lifecycle.ViewModelProvider.get(ViewModelProvider.java:130)
at com.test.fragments.CategoriesFragment.onViewCreated(CategoriesFragment.kt:38)
at androidx.fragment.app.FragmentManagerImpl.moveToState(FragmentManagerImpl.java:892)
at androidx.fragment.app.FragmentManagerImpl.moveFragmentToExpectedState(FragmentManagerImpl.java:1238)
at androidx.fragment.app.FragmentManagerImpl.moveToState(FragmentManagerImpl.java:1303)
at androidx.fragment.app.BackStackRecord.executeOps(BackStackRecord.java:439)
at androidx.fragment.app.FragmentManagerImpl.executeOps(FragmentManagerImpl.java:2079)
at androidx.fragment.app.FragmentManagerImpl.executeOpsTogether(FragmentManagerImpl.java:1869)
at androidx.fragment.app.FragmentManagerImpl.removeRedundantOperationsAndExecute(FragmentManagerImpl.java:1824)
at androidx.fragment.app.FragmentManagerImpl.execPendingActions(FragmentManagerImpl.java:1727)
at androidx.fragment.app.FragmentManagerImpl.dispatchStateChange(FragmentManagerImpl.java:2663)
at androidx.fragment.app.FragmentManagerImpl.dispatchActivityCreated(FragmentManagerImpl.java:2613)
at androidx.fragment.app.FragmentController.dispatchActivityCreated(FragmentController.java:246)
at androidx.fragment.app.FragmentActivity.onStart(FragmentActivity.java:542)
at androidx.appcompat.app.AppCompatActivity.onStart(AppCompatActivity.java:201)
at android.app.Instrumentation.callActivityOnStart(Instrumentation.java:1333)
at android.app.Activity.performStart(Activity.java:6992)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2780)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2892)
at android.app.ActivityThread.-wrap11(Unknown Source:0)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1593)
at android.os.Handler.dispatchMessage(Handler.java:105)
at android.os.Looper.loop(Looper.java:164)
at android.app.ActivityThread.main(ActivityThread.java:6541)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:240)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:767)
Caused by: java.lang.NoSuchMethodException: <init> [class android.app.Application]
at java.lang.Class.getConstructor0(Class.java:2320)
at java.lang.Class.getConstructor(Class.java:1725)
at androidx.lifecycle.ViewModelProvider$AndroidViewModelFactory.create(ViewModelProvider.java:230)
Earlier my BaseViewModel was extendingViewModel
. Later I had to change it AndroidViewModel
as I am using Room
in my project and for DB initialization it needs the context.
You have to create custom ViewModelProvider.Factory
to achieve parameterized ViewModel
. Check below:
public class MyViewModelFactory implements ViewModelProvider.Factory {
private MyApplication mApplication;
public MyViewModelFactory(MyApplication application) {
mApplication = application;
}
@Override
public <T extends ViewModel> T create(Class<T> modelClass) {
if(modelClass == DataViewModel.class)
return (T) new DataViewModel(mApplication);
throw new IllegalArgumentException("Unknown ViewModel class");
}
}
class MyViewModelFactory(private val mApplication: MyApplication) : ViewModelProvider.Factory {
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
return when(modelClass) {
DataViewModel::class.java -> DataViewModel(mApplication)
else -> throw IllegalArgumentException("Unknown ViewModel class")
} as T
}
}
Now initiate your ViewModel
like below:
DataViewModel dataViewModel = ViewModelProviders
.of(this, new MyViewModelFactory(this.getApplication()))
.get(DataViewModel.class);
val dataViewModel = ViewModelProviders
.of(this, MyViewModelFactory(this.application))
.get(DataViewModel::class.java)