Android ViewModel injections revisited
Tags: android, explainlikeimfive, beginners, tips
Published: 2018-08-31 07:14:59

---
title: Android ViewModel injections revisited
published: true
description: In one of our previous posts we have described how to implement a ViewModel factory that was able to provide ViewModels with their dependencies injected, e.g. an API client, and it was good enough for us at that time. Later on, thanks to Piotr, we’ve found out even better and simpler approach with an additional possibility of injecting Activity- or Fragment-dependant data into ViewModels.
tags: Android, explainlikeimfive, beginners, tips 
---

In one of [our previous posts](https://brightinventions.pl/blog/injectable-android-viewmodels/) we have described how to implement a ViewModel factory that was able to provide ViewModels with their dependencies injected, e.g. an API client, and it was good enough for me at that time. Later on, thanks to [Piotr](https://miensol.pl/), we've found out even better and simpler approach with an additional possibility of injecting Activity- or Fragment-dependant data into ViewModels.

![Vaccine](https://thepracticaldev.s3.amazonaws.com/i/mdhj3sbe21dxobzwi6mm.jpg)

# Simpler factory #

Previously, we've created a singleton factory that was supplied with a map of `ViewModel`-based classes and their respective `Provider`s. It required us to create a custom `ViewModelKey` annotation and use Dagger to generate the map using `IntoMap` bindings. It didn't require a lot of boilerplate code compared to some other solutions I saw at that time, but it wasn't perfect either.

On the contrary, the new solution is based on a generic ViewModel factory class of which instances are created for each Activity or Fragment instance.

``` kotlin
import android.arch.lifecycle.ViewModel
import android.arch.lifecycle.ViewModelProvider
import dagger.Lazy
import javax.inject.Inject

class ViewModelFactory<VM : ViewModel> @Inject constructor(
    private val viewModel: Lazy<VM>
) : ViewModelProvider.Factory {

    @Suppress("UNCHECKED_CAST")
    override fun <T : ViewModel> create(modelClass: Class<T>): T {
        return viewModel.get() as T
    }
}
```

For example (see the [full code here](https://github.com/azabost/simple-mvvm-example)):

```kotlin
class MainViewModel @Inject constructor(
    private val apiClient: ApiClient
) : ViewModel() {
    // ...
}

class MainActivity : BaseActivity() {

    @Inject
    lateinit var vmFactory: ViewModelFactory<MainViewModel>

    lateinit var vm: MainViewModel

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        vm = ViewModelProviders.of(this, vmFactory)[MainViewModel::class.java]

        // ...
    }
}
```

As you can see, there is much less code and personally I think it's also easier to understand. To make it even more concise, we can add an extension function in the `BaseActivity` class like this:

```kotlin
abstract class BaseActivity : AppCompatActivity() {
    // ...

    inline fun <reified T : ViewModel> ViewModelFactory<T>.get(): T =
        ViewModelProviders.of(this@BaseActivity, this)[T::class.java]
}
```

Then, we can get a ViewModel with just: `vm = vmFactory.get()`

Analogically, we can add a similar function for Fragments.

# More possibilities #

One of the issues we've had was that the singleton factory holding a map of ViewModel providers was widely scoped, therefore it wouldn't let us inject anything coming from a more narrow scope, e.g. Activity's extras or Fragment's arguments.

Creating a new factory each time makes it possible. In order to achieve this, we need an additional module that knows how to obtain the dependencies. For example:

```kotlin
import com.azabost.simplemvvm.net.response.RepoResponse
import dagger.Module
import dagger.Provides

@Module
class RepoActivityIntentModule {
    @Provides
    fun providesRepoResponse(activity: RepoActivity): RepoResponse {
        return activity.intent.getSerializableExtra(RepoActivity.REPO_RESPONSE_EXTRA) as RepoResponse
    }
}
```

This module must then be added to the respective `RepoActivity` subcomponent generated by the `ContributesAndroidInjector` annotation:

```kotlin
import com.azabost.simplemvvm.ui.main.MainActivity
import com.azabost.simplemvvm.ui.repo.RepoActivity
import com.azabost.simplemvvm.ui.repo.RepoActivityIntentModule
import dagger.Module
import dagger.android.ContributesAndroidInjector

@Module
abstract class AndroidInjectorsModule {
    @ContributesAndroidInjector
    abstract fun contributeMainActivity(): MainActivity

    @ContributesAndroidInjector(modules = [RepoActivityIntentModule::class])
    abstract fun contributeRepoActivity(): RepoActivity
}
```

Finally, when we get our `RepoViewModel` in the `RepoActivity`, it has the data coming from the intent already injected:

```kotlin
class RepoViewModel @Inject constructor(
    val repoResponse: RepoResponse
) : ViewModel()

class RepoActivity : BaseActivity() {

    @Inject
    lateinit var vmFactory: ViewModelFactory<RepoViewModel>

    lateinit var vm: RepoViewModel

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_repo)

        vm = ViewModelProviders.of(this, vmFactory)[RepoViewModel::class.java]

        repoData.text = vm.repoResponse.id.toString()
    }

    companion object {
        const val REPO_RESPONSE_EXTRA = "REPO_RESPONSE_EXTRA"
    }
}
```

Originally published at [brightinventions.pl](https://brightinventions.pl/blog/)


By Andrzej Zabost, Android Developer at Bright Inventions
[Blog](https://azabost.com/), [Email](andrzej.zabost@brightinventions.pl)