Dagger for Android: A Beginner’s Guide to Multibindings

Avishek Khan
Monstar Lab Bangladesh Engineering
6 min readSep 17, 2019

The happy marriage between ViewModel and Dagger using @IntoMap

Photo by Thanh Tran on Unsplash

One particular challenge we face when working with ViewModel architecture component together with Dagger is that there’s no straightforward way to provide a ViewModel dependency having non-zero argument constructor that will be conscious of the lifecycle associated with the injection target such as activity or fragment. One solution to this problem is to implement a ViewModelProvider.Factory which can be used to instantiate ViewModels in a lifecycle-aware way. This implementation of ViewModelProvider.Factory needs a Map dependency that Dagger has to satisfy. The way we achieve this resolution is by using multibindings.

Multibinding is a way of binding several objects of some given type into a collection that application code can inject without depending on the individual bindings directly. To make things easy, Dagger comes with a few annotations such as @IntoSet, @ElementsIntoSet and @IntoMap for different use cases.

Note: henceforth in this post, I’m going to write Factory instead of ViewModelProvider.Factory to keep it short and easier to read.

The map multibinding is especially useful if we want to create a Factory instance. We have to bind all the ViewModels into a map using the @IntoMap annotation. Dagger will manage the map itself and provide as the dependency of Factory implementation when we tell it to. We’ll see how it can be done in the examples of this article. But before that, let’s take a step back and see why exactly we need this.

Note: If you want to learn about @IntoSet and @ElementsIntoSet, you can start from here.

Why @IntoMap?

There are a few ways you can create a ViewModel instance. Since it’s a lifecycle-aware component, it needs a reference of the lifecycle owner such as an activity or fragment. You can instantiate a ViewModel object from an activity or fragment in the following manner to make it lifecycle-conscious:

val viewModel = ViewModelProviders.of(this)[YourViewModel::class.java]

Sweet! But there’s a catch — YourViewModel class cannot have any parameter in its constructor. But, this is impractical, in real-life scenarios, it can have any number of parameters such as repository, or if you’re into the clean architecture, one or more use cases. For non-zero argument constructors, you can instantiate the object in the age-old way:

val viewModel = YourViewModel(argA, argB)

Can you see the problem here? The ViewModel isn’t aware of the lifecycle owner in this approach and therefore it will not survive activity recreation. This is basically the MVP pattern where ViewModel is taking the presenter’s job. This is where the Factory interface comes into play. You can implement it and override the create() function to instantiate your ViewModel (we’ll come to the details in a while) and write something like the following in your activity or fragment class:

val viewModel = ViewModelProviders.of(this, FactoryImplementation())[YourViewModel::class.java]

Instead of creating a Factory instance every time, you can make it a singleton that Dagger will provide to our injection targets, i.e., activities and fragments. Let’s see how.

But before starting off, I would recommend you clone the repository and checkout part-3 branch. All the examples given in this post are from this project. If you’ve been following this series, you should know by now that I created the project based on dagger-android and the latest version of Dagger as of September 2019 (v2.24) so that you can write the code described in the article and learn Dagger interactively. The stuff necessary for this part was already implemented in the previous post and the complete source code can be found in the aforementioned branch. If this is your first time here, you can check out part 1 and part 2 where I covered initial setup for Dagger, providing dependencies and scoped bindings. This post is written on the assumption that you already have the basic idea about these things.

Implementing ViewModelProvider.Factory

Create a class that implements ViewModeProvider.Factory and overrides create() function. Add a Map<Class<out ViewModel>, Provider<ViewModel>> parameter to its constructor as the dependency. For example, take a look inside the ViewModelFactory.kt file which contains all the implementation details. Since we want to instantiate the class only once in our app’s lifetime, annotate it with the same scope as our AppComponent’s — @Singleton:

Now it’s time to tell Dagger how to resolve this dependency by providing the map.

Creating the map key

Create an annotation class that Dagger will use to create the key of the map. For example, take a look inside ViewModelKey.kt which is used to tell Dagger that the key of the map will be a ViewModel Class instance:

Binding into map

Create an abstract @Module class and write an abstract function inside it to bind your ViewModel into the map using @ViewModelKey. The return type of the function tells Dagger that the value of the map will be a Provider<ViewModel> instance and the key will be the Class instance of your ViewModel. Take a look inside the ViewModelModule.kt file to see it in action:

In your own projects, write an abstract function for each of your ViewModels in this fashion. Make sure the ViewModel implementation has an @Inject-annotated constructor so that Dagger knows how to provide the instance.

Since the binding of MainViewModel is now done inside a module installed into @Singleton-scoped AppComponent, we have to disassociate MainViewModel from @ActivityScope by removing the scope annotation.

We want to inject our MainActivity and CatFragment with the Factory instance. So we need to bind Factory to its implementation — ViewModelFactory:

Add the module to AppComponent:

Injecting MainActivity and CatFragment

Now replace the mainViewModel property with viewModelFactory of type ViewModelProvider.Factory in both MainActivity and CatFragment:

@Inject
lateinit var viewModelFactory: ViewModelProvider.Factory

In onCreate() function of MainActivity, instantiate MainViewModel using this statement:

And in onViewCreated() of CatFragment:

Now run the app. If you rotate the screen now, the activity will be recreated, but it will get the same ViewModel instance and the list already fetched from the server will be dispatched by the LiveData without any need for additional network call.

In case you haven’t noticed, now the CatFragment subcomponent doesn’t have any dependency on any of its parent component’s scoped bindings. Therefore, it doesn’t need to be a child of MainActivity component anymore. As long as all the activities and fragments in your projects depend only on Factory or any other @Singleton-scoped object, the subcomponent can be a child of AppComponent. That means, MainFragmentBindingModule and ActivityBindingModule can be installed independently into AppComponent. In other words, CatFragment and MainActivity subcomponents can be mutually independent sibling components. In this way, you can bind as many fragment subcomponents as you like inside the MainFragmentBindingModule. Since it’s no longer installed into the MainActivity subcomponent, you can remove the prefix ‘Main-’ from its name and simply call it FragmentBindingModule that would contain all the fragment subcomponent bindings of your project.

You can find the complete source code of this article in the branch part-3-complete:

You can also find part 1 of the series here:

And part 2 here:

Please do share your thoughts. You can reach out to me on Twitter: @Avishek_Khan. You might also want to check out more tech posts on our blog. Until next time, happy learning!

--

--

Avishek Khan
Monstar Lab Bangladesh Engineering

Software Engineer. I love Java as a coffee drink and Kotlin as a programming language. Passionate about science, technology, fitness, traveling, and tequila.