Search code examples
androidmvvmandroid-viewmodel

Android view no creating: No Such Method Error


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.


Solution

  • You have to create custom ViewModelProvider.Factory to achieve parameterized ViewModel. Check below:

    • In Java:
    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");
        }
    }
    
    • In Kotlin:
    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:

    • In Java:
    DataViewModel dataViewModel = ViewModelProviders
        .of(this, new MyViewModelFactory(this.getApplication()))
        .get(DataViewModel.class);
    
    • In Kotlin:
    val dataViewModel = ViewModelProviders
        .of(this, MyViewModelFactory(this.application))
        .get(DataViewModel::class.java)