1

How to recreate ViewModel that was created with params injected via Factory after instance of activity is recreated?

ViewModel has a constructor like this:

class MyViewModel(
        val model: MyModel,
        val repository: MyRepository
) : ViewModel()

I instantiate it with factory:

ViewModelProviders
                    .of(this, MyViewModelFactory(
                            model = MyModel()
                            repository = MyRepository()))
                    .get(MyViewModel::class.java)

I tried to recover ViewModel like this, while savedInstanceState is not null (activity is recreated):

ViewModelProviders
                    .of(this)
                    .get(MyViewModel::class.java)

This causes a crash because no 0 arguments constructor is present inside MyViewModel class.

As, for passing factory each time: My problem with that is, that whatever I pass as a MyModel to ViewModel, and that comes from activity Intent, might change later, due to user interaction. That would mean, when recreating, the MyModel in the Intent is outdated to the model that is already stored in ViewModel and was tampered by user interaction.

Jacek Kwiecień
  • 12,397
  • 20
  • 85
  • 157
  • Refer to this - https://stackoverflow.com/questions/54419236/why-a-viewmodel-factory-is-needed-in-android/54420034#54420034 – Vishal Arora Jul 25 '19 at 07:24
  • Your ViewModel would be preserved (along with the data it is holding) during activity recreation. You don't have to recreate it after activity recreation, Android system would do that for you. – Praveen Singh Jul 29 '19 at 08:53

3 Answers3

2

This causes a crash because no 0 arguments constructor is present inside MyViewModel class.

It'll crash, as you're not passing any factory to construct the ViewModel.

How to recreate ViewModel that was created with params injected via Factory after instance of activity is recreated?

AFAIK, you don't have to manually recreate the ViewModel on savedInstanceState. You may use viewModel to store data that are used in the activity.So, on recreation of the activity, the ViewModelProvider will not create a new instance of the viewModel but will give you the old instance and the data held in the viewModel will not be cleared.So there's no need to worry about savedInstanceState.

TIP: If you want to manage the creation of the factory and improve the recreation process. you may check this article on ViewModel with Dagger

theapache64
  • 10,926
  • 9
  • 65
  • 108
  • I think `model` and `repository` may be dependent on `savedInstanceState` value but not the viewmodel. – Arka Prava Basu Jul 25 '19 at 07:39
  • 1
    I believe, `model` and `repository` shouldn't be visible to `Activity` and should be accessed via `viewModel`. Why should we use `savedInstanceState` ? @ArkaPravaBasu – theapache64 Jul 25 '19 at 07:51
  • I believe principles of DI says the opposite. If a ViewModel is dependent on a repository and model, those should be injected into it instead of ViewModel taking up the responsibility of instantiating them. The `savedInstanceState` flag use is implementation and use case specific. Without actually knowing what data `Model` stores and what data `Repository` needs I am not sure in which way I would use it. – Arka Prava Basu Jul 25 '19 at 07:58
0

There are no 0 argument constructors in MyViewModel. When you try to get the instance of ViewModel without providing a Factory it will look for a 0 argument constructor.

You can use this regardless of whether savedInstanceState is null or not.

ViewModelProviders
                    .of(this, MyViewModelFactory(
                            model,repository))
                    .get(MyViewModel::class.java)

Rather how you create repository and model changes on the basis of savedInstanceState value, depending on your use case or implementation.

Arka Prava Basu
  • 2,366
  • 3
  • 18
  • 34
0

Probably the only answer is, that it cannot be done this way. If the factory was provided, it has to be provided at all time. I don't know mechanism underneath ViewModel recreation but these are not as smart as I hoped.

My current implementation looks like this:

    viewModel = ViewModelProviders
            .of(this, MyFactory(MyRepository()))
            .get(MyMViewModel::class.java)

    val binding = DataBindingUtil.setContentView<ActivityCreateFoodstuffBinding>(this, R.layout.my_activity)
    binding.viewModel = viewModel

    if (savedInstanceState == null) {
        val model = intent.getParcelableExtra<MyModel>("model")
        viewModel.model.set(model)
    }

I use one param constructor in ViewModel that always takes repository, but model I moved away and set it only when the activity is freshly created.

Jacek Kwiecień
  • 12,397
  • 20
  • 85
  • 157