I am trying to write a sample app using Android architecture components and but even after trying for days I could not get it to work. It gives me the above exception.
In my case the reason was that I was trying to get shared instance of the ViewModel in my fragment to soon - before the activity was created. That happens when application is restoring its state after being killed.
Preconditions:
Code in activity:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
//factory is constructed using Dagger
val factory = App.get().components().appComponent.getMapViewModelFactory()
//activity creates the instance of MapViewModel
viewModel = ViewModelProviders.of(this, factory)[MapViewModel::class.java]
}
Code in fragment:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
//fragment receives the instance of MapViewModel
viewModel = ViewModelProviders.of(activity!!)[MapViewModel::class.java]
...
}
When I open the app for the first time, everything works fine: activity creates instance of ViewModel; I open Fragment, which get the instance of ViewModel. But when when application is trying to restore its state after being killed, first it calls the body of onCreate of the Fragment and then the body of onCreate of the Activity. At that point fragment can't get the ViewModel as Activity had not created it yet.
Solution 1: Move the code when fragment get the ViewModel from onCreate to onViewCreated. Which is fine as I observe all liveDatas in onViewCreated as well.
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
viewModel = activity?.run { ViewModelProviders.of(this)[MapViewModel::class.java] } ?: throw Exception("Invalid Activity")
viewModel.getSurveyDateLiveData().observe(viewLifecycleOwner, Observer { dateTextView.text = it })
...
}
Solution 2: Create the instance of ViewModel in Activity.onCreate before super.onCreate is called. In this case you can get the ViewModel in your fragment's onCreate.
override fun onCreate(savedInstanceState: Bundle?) {
val factory = App.get().components().appComponent.getMapViewModelFactory()
viewModel = ViewModelProviders.of(this, factory)[MapViewModel::class.java]
super.onCreate(savedInstanceState)
Timber.d("cc: onCreate: $this ")
}
Solution 3:
If you are injecting repository instance in your ViewModel, Check that you are not using @Inject constructor(...):ViewModel()
to inject your repository, but rather **@ViewModelInject constructor(...):ViewModel()**