Nader Besada
Nader Besada

Reputation: 389

What is the point of injecting a ViewModelFactory in Android - Dagger 2

I have recently started with Android Development, and coming from an iOS background, concepts like Dependency Injection frameworks and ViewModelFactories are a new thing for me. In all the tutorials I saw, ViewModels always extended the ViewModel class provided by android.arch.lifecycle.ViewModel. If the viewModel had parameters then a ViewModelFactory had to be created and injected in an activity by Dagger

@Provides
@ActivityScope
fun provideViewModelFactory(dependency: Dependency) : ViewModelProvider.Factory = CustomViewModelFactory(dependency)

CustomViewModelFactory will then be in charge of creating ViewModels. This all works fine, however, I'm not really understanding the point of a viewModelFactory when I can just inject the viewModels directly into an activity sort of like this:

@Module
class ViewModelModule(private val user: User) {
    @ActivityScope
    @Provides
    fun provideMainViewModel() = MainViewModel(user = user)
    fun provideOtherViewModel() = OtherViewModel()
}

@ActivityScope
@Subcomponent(modules = [ViewModelModule::class])
interface MainActivitySubComponent {
    fun inject(activity: MainActivity)
}

@ApplicationScope
@Component()
interface ApplicationComponent {
    fun addMainActivitySubComponent(viewModelModule: ViewModelModule) : MainActivitySubComponent
}

And in my Activity

class MainActivity : AppCompatActivity() {

    @Inject lateinit var viewModel: MainViewModel

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        val subComponent = (application as MainApplication).component.addMainActivitySubComponent(ViewModelModule(User("NEW NAME")))

        subComponent.inject(this)
    }
}

So what is the point of a ViewModelFactory when I can simply inject a viewModel to in my activity right away?

Upvotes: 8

Views: 6574

Answers (2)

David Medenjak
David Medenjak

Reputation: 34542

Let's take a look at what happens when you inject your ViewModel:

  1. Your Activity gets (re)created.
  2. You create a (Sub)Component for said Activity.
  3. You inject the dependencies.

The interesting part is 3, where we will inject some scoped objects (e.g. Singletons) but create new objects for the rest of our dependencies.

Every time you (re)create your Activity (-> configuration change) those objects will be created again, and you'll lose state. You can use savedInstanceState to preserve some data, or you could figure out some other means to save state (e.g. Singletons, retained Fragments, ...)


ViewModel on the other hand promises to keep state between orientation changes. When we request a ViewModel from ViewModelProviders after an orientation change we will receive the same object as before. We don't need to worry about saving and restoring the state.

We would recreate the factory, but Android/ the Support Library/Jetpack/Arch Components (whatever it's called now) will store the Viewmodel and only create it when it hasn't been created before. The previous model will be reused during configuration changes.


So if you want to inject the ViewModel directly you can obviously do so, but your ViewModel won't keep its state between orientation changes.

Upvotes: 6

EpicPandaForce
EpicPandaForce

Reputation: 81588

You use the ViewModelProviders and the ViewModelProviders.Factory to ensure that you get the same instance of ViewModel across configuration changes. So in the scope of an Activity, the ViewModel created by the ViewModelProviders is only created once.

It's also required for expected behavior of the onCleared() callback that ViewModels have.

In Kotlin, I prefer to use this linked approach compared to the multi-binding one. Although it does require you to know about the "injector" who can create the view model.

Upvotes: 5

Related Questions