In my case, the reason was that I was trying to get a shared instance of the ViewModel in my fragment too soon - before the activity was created. What happens when the application is restoring its state after being killed.
Preconditions:
- My ViewModel has a public constructor.
- My ViewModel has multiple arguments. But this is absolutely fine as I use ViewModelFactory to construct the ViewModel.
- My Fragment and Activity shares the same instance of the ViewModel. In other words: Activity creates the ViewModel and the fragment receives the same instance later.
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 an instance of ViewModel; I open Fragment, which gets the instance of ViewModel. But when the 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, the fragment can't get the ViewModel as Activity had not created it yet.
Solution 1: Move the code when the fragment gets the ViewModel from onCreate to onViewCreated. This is fine as I observe all live data 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<String> { 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()**