Dagger for Android: A Beginner’s Guide to Multibindings
The happy marriage between
ViewModeland Dagger using
One particular challenge we face when working with
ViewModelarchitecture 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.Factorywhich can be used to instantiate
ViewModels in a lifecycle-aware way. This implementation of
Mapdependency 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
@IntoMapfor different use cases.
Note: henceforth in this post, I’m going to write
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
@ElementsIntoSet, you can start from here.
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 —
YourViewModelclass 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-3branch. 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-androidand 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.
Create a class that implements
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.ktfile 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
Now it’s time to tell Dagger how to resolve this dependency by providing the map.
Creating the map key
annotationclass that Dagger will use to create the key of the map. For example, take a look inside
ViewModelFactory.kt which is used to tell Dagger that the key of the map will be a
Binding into map
Create an abstract
@Moduleclass 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
ViewModelFactory.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
MainViewModelis now done inside a module installed into
AppComponent, we have to disassociate
@ActivityScopeby removing the scope annotation.
We want to inject our
Factory instance. So we need to bind
Factory to its implementation —
Add the module to
Injecting MainActivity and CatFragment
Now replace the
mainViewModel property with
viewModelFactory of type
@Inject lateinit var viewModelFactory: ViewModelProvider.Factory
MainViewModel using this statement:
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
LiveDatawithout 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
Factoryor any other
@Singleton-scoped object, the subcomponent can be a child of
AppComponent. That means,
ActivityBindingModulecan be installed independently into
AppComponent. In other words,
MainActivitysubcomponents 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
MainActivitysubcomponent, you can remove the prefix ‘Main-’ from its name and simply call it
FragmentBindingModulethat would contain all the fragment subcomponent bindings of your project.
You can find the complete source code of this article in the branch
You can also find part 1 of the series here:
And part 2 here: