4

I have built a fragment that basically contains a Recyclerviewer with a custom ViewModel for my Data type using data binding and LiveData (which is a piece of code that I frequently implement) but in this time a runtime exception error has occurred!

/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.example.tdm_project, PID: 9460
    java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example.tdm_project/com.example.tdm_project.MainActivity}: java.lang.RuntimeException: Cannot create an instance of class com.example.tdm_project.viewmodel.ArticleViewModel
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2734)
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2799)
        at android.app.ActivityThread.-wrap12(ActivityThread.java)
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1537)
        at android.os.Handler.dispatchMessage(Handler.java:110)
        at android.os.Looper.loop(Looper.java:203)
        at android.app.ActivityThread.main(ActivityThread.java:6269)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1063)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:924)
     Caused by: java.lang.RuntimeException: Cannot create an instance of class com.example.tdm_project.viewmodel.ArticleViewModel
        at androidx.lifecycle.ViewModelProvider$NewInstanceFactory.create(ViewModelProvider.java:154)
        at androidx.lifecycle.ViewModelProvider$AndroidViewModelFactory.create(ViewModelProvider.java:211)
        at androidx.lifecycle.ViewModelProvider.get(ViewModelProvider.java:135)
        at androidx.lifecycle.ViewModelProvider.get(ViewModelProvider.java:103)
        at com.example.tdm_project.HomeFragment.onCreateView(HomeFragment.kt:75)
        at androidx.fragment.app.Fragment.performCreateView(Fragment.java:2439)
        at androidx.fragment.app.FragmentManagerImpl.moveToState(FragmentManager.java:1460)
        at androidx.fragment.app.FragmentManagerImpl.moveFragmentToExpectedState(FragmentManager.java:1784)
        at androidx.fragment.app.FragmentManagerImpl.moveToState(FragmentManager.java:1852)
        at androidx.fragment.app.BackStackRecord.executeOps(BackStackRecord.java:802)
        at androidx.fragment.app.FragmentManagerImpl.executeOps(FragmentManager.java:2625)
        at androidx.fragment.app.FragmentManagerImpl.executeOpsTogether(FragmentManager.java:2411)
        at androidx.fragment.app.FragmentManagerImpl.removeRedundantOperationsAndExecute(FragmentManager.java:2366)
        at androidx.fragment.app.FragmentManagerImpl.execPendingActions(FragmentManager.java:2273)
        at androidx.fragment.app.FragmentManagerImpl.dispatchStateChange(FragmentManager.java:3273)
        at androidx.fragment.app.FragmentManagerImpl.dispatchActivityCreated(FragmentManager.java:3229)
        at androidx.fragment.app.FragmentController.dispatchActivityCreated(FragmentController.java:201)
        at androidx.fragment.app.FragmentActivity.onStart(FragmentActivity.java:620)
        at androidx.appcompat.app.AppCompatActivity.onStart(AppCompatActivity.java:178)
        at android.app.Instrumentation.callActivityOnStart(Instrumentation.java:1248)
        at android.app.Activity.performStart(Activity.java:6683)
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2697)
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2799) 
        at android.app.ActivityThread.-wrap12(ActivityThread.java) 
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1537) 
        at android.os.Handler.dispatchMessage(Handler.java:110) 
        at android.os.Looper.loop(Looper.java:203) 
        at android.app.ActivityThread.main(ActivityThread.java:6269) 
        at java.lang.reflect.Method.invoke(Native Method) 
        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1063) 
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:924) 
     Caused by: java.lang.InstantiationException: java.lang.Class<com.example.tdm_project.viewmodel.ArticleViewModel> has no zero argument constructor
        at java.lang.Class.newInstance(Native Method)

After googling my problem, the only suggestion I found is to implement a ViewModelFactory even though am using a ViewModel class and not AndroidViewModel.

See This answer

ArticleViewModel class



class ArticleViewModel : ViewModel {

    //lists
    private var articleMList = MutableLiveData<ArrayList<ArticleViewModel>>()
    private var articleInnerList = ArrayList<ArticleViewModel>()

    constructor(
     article: Article
    ) : super() {
        //const with parameters
    }

    //to observe my list
    fun getArticles() : MutableLiveData<ArrayList<ArticleViewModel>>{

        articleMList.value = articleInnerList

        return articleMList
    }

    //retrieve data from backend
    fun getData() {
            //some code
        }
}

Fragment class


class HomeFragment : Fragment() {


    //viewmodel
    private lateinit var vmodel : ArticleViewModel



    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {

        //set the view
        rootView = inflater.inflate(R.layout.home_fragment, container, false)

         //a function to initialize my recyclerview
        intialiserHorizontally()

          //creating the instance of viewmodel
        vmodel = ViewModelProviders.of(activity!!).get(ArticleViewModel::class.java)

        vmodel.getArticles().observe(this, Observer {
             customHAdapter.swapData(it)
        })

//getting the data from my db
        vmodel.getData()




        return rootView
    }

My question is why it is necessary to implement a ViewModelProvider.Factory in this case?

Zahra
  • 88
  • 1
  • 9
  • "but in this time a runtime exception error has occurred!" -- please edit your question and provide the stack trace. – CommonsWare Aug 25 '19 at 18:27
  • 1
    Your `ArticleViewModel` code in your question will not compile, so what you have in your project differs from what you have in your question. Hence, we will have to guess. I suggest removing your secondary constructor, as you are not using it and do not need it, at least based on the code in your question. If you want that constructor and expect to use it, *that* is why you would need a factory, as the `ViewModel` system knows nothing about that constructor or how to call it. – CommonsWare Aug 25 '19 at 18:40
  • Oh yes, it worked once I added another constructor without any parameter. Thank you ! – Zahra Aug 25 '19 at 18:51

2 Answers2

3

On its own, the ViewModel system only knows how to use a zero-argument constructor on your ViewModel subclasses (or a single-parameter constructor for AndroidViewModel subclasses). So, either:

  • Have no constructors at all, or

  • Have an explicit zero-argument constructor (but consider getting rid of your other one, as nothing should use it), or

  • Implement a factory, so you can call your desired custom constructor

CommonsWare
  • 986,068
  • 189
  • 2,389
  • 2,491
0

Generally, if you want to create an instance of the ViewModel by ViewModelProviders.of(activity).get(ArticleViewModel::class.java) then it expects zero-argument constructor of the ViewModel. If you want to pass any dependency to the ViewModel constructor then you have to use a factory and then you can instantiate by ViewModelProviders.of(activity, factory).get(ArticleViewModel::class.java).

Suggestion:

  1. Use dagger for dependency injection (it will make your life easy :))
  2. Use list of data class private var articlesLiveData = MutableLiveData<List<Article>>() instead of list of ViewModels in your ViewModel.
Abu Noman
  • 435
  • 3
  • 13