MVVM Architecture for Android consuming Marvel API Part 2: Dagger2 and Dependency Injection

Matteo Pasotti
6 min readOct 21, 2018

In the first part of the series we talked about MVVM design pattern, and how its different parts interact between each other. We also explored how to configure Room in your project.

In this part I’ll explain how to use Dagger2 to provide dependencies in our application. I highly recommend that you read dagger 2 tutorials and understand what dependency injection is, otherwise it could be pretty hard to understand this article.

Every piece of code is in this sample:

Interaction Diagram

Let’s see which our main parts are and how they interact between each other, then I’ll elaborate further.

We mainly have two modules which provide our dependencies: AppModule and NetModule. The first one provides the instance of our database, and its Daos. This module could provide some other main dependencies like the Preferences. The other one, NetModule, provides everything linked to the Network (Retrofit instance, Interceptor, Authenticator, MarvelApi etc.).

In the other 3 modules, we basically define where we want to bind our dependencies (Activities, Fragments and ViewModules), thus where we need these dependencies.

Finally, our Application class will build AppComponent.

We’ll see them detailed in the next section…

AppComponent

A class annotated with @Component is a bridge between @Module and @Inject. It’s a common practice to create a main component called AppComponent which provides the main dependencies. This component is the root of our dagger graph and it provides different modules. Let’s have a look:

Before describing the modules, I’ll explain some annotations used here. Components can be instantiated by using the Builders generated by Dagger, this was the old way:

DaggerAppComponent appComponent = DaggerAppComponent.builder()
.appModule(new AppModule(this)) //this : application
.netModule(new NetModule())
.activityModule(new ActivityModule())

......
.build();

Now we can customise the generated builder by utilising something known as Component.Builder.

A builder for a component. Components may have a single nested static abstract class or interface annotated with @Component.Builder. If they do, then the component's generated builder will match the API in the type

So using it, Dagger generates the same class Builder as before! We can then customise this Component.Builder using @BindsInstance.

@BindsInstance methods should be preferred to writing a @Module with constructor arguments and immediately providing those values — source

To understand it properly, let’s do an example with AppModule which will provide the main dependencies of our Application, like Preferences, Room etc and it used to provide the Application Context provided as constructor arguments :

public AppModule(Application application) {
this.application = application;
}

Let’s look at our simplified AppModule with the constructor and @Provides Application removed:

So... where is the application instance?? If you look the AppComponent class, you’ll notice that it’s provided by the BindInstance!

@BindsInstance
fun application(application: Application): AppComponent.Builder

We don’t need to specify Builder appModule(AppModule appModule);inside the Component.Builder, as we are going to let Dagger use the default constructor of AppModule now. So, when we instantiate the AppComponent inside the Application class, it will be like this:

DaggerAppComponent.builder().application(this).build().inject(this)

That’s it, we’ve just seen our AppComponent, some annotations with their meanings and effects (Component.Builder , BindInstance), the AppModule and how to instantiate our component.

AndroidSupportInjectionModule: I didn’t create this, it is an internal class in Dagger 2.10. It provides our activities and fragments with a given module.

ActivityModule

If you’ve used Dagger before probably you noticed that it introduces a lot of boilerplate code, especially when we create subcomponents for each Activity, Fragment, Service etc. Dagger has introduced an annotation which reduces it, it’s @ContributesAndroidInjector and attaches activities and fragments directly to your dagger graph. So, for example, if we have HomeActivity and we want to generate a HomeSubComponent, we’ll write this line of code inside a module:

@ContributesAndroidInjector
internal abstract fun contributeHomeActivity(): HomeActivity

It’s good practice to create a module to handle all such cases and include it in our modules, and this is what Idid using ActivityModule:

Another improvement using this annotation is about how we inject and use the components inside Activities, Fragments etc. For example, we used to create them directly inside an Activity, then we managed them using SavedInstanceState etc. A class should never know how it is being injected.

Thus, let’s see how our Activity was before, and how it has been changed using @ContributesAndroidInjector:

Old Way
New Way

The final things to know about this amazing annotation are provided by the official documentation:

  • “Generates an AndroidInjector for the return type of this method” : It will generate AndroidInjector<HomeActivity>.
  • This annotation must be applied to an abstract method in a Module that returns a concrete Android framework type.” i.e. Activity / Fragment, etc.

FragmentModule

Here it’s the same but with Fragments, Dagger attaches them to Dagger Graph always using ContributesAndroidInjector.

For Fragments, we initialise our dependencies in onAttach()

@Override
public void onAttach(Context context) {
AndroidSupportInjection.inject(this);
super.onAttach(context);
}

ViewModelModule

To understand this last module and what we’re doing here, I’ll try to explain how we can use Dagger2 multibindings with ViewModel.

For building customViewModel classes with argument-passing constructors (e.g. for passing custom data or @Inject annotated constructors), we must provide a class that extends ViewModelProvider.Factory, returning instances of our custom ViewModels into the create() method. This class is AppViewModelFactory:

Let’s try to understand this class and what it does. The first thing is the constructor which takes Map<Class<? extends ViewModel>, Provider<ViewModel>> as parameter. It’s a map which has a Class that extends ViewModel as key, and a Provider of ViewModel (a Dagger 2-specific class that let us to provide — and so instantiate — a dependency-injected class) as a value. Thus, a Provider can give us the ViewModel of a determinate type.

How can we use this Provider and where ?

The answer is the create method of our custom ViewModelProvider.Factory , which will return an instance of the ViewModel. This method takes the type T of the ViewModel, that was requested from an Activity or Fragment, as parameter. If inside our Map we have a Provider which can instantiate a ViewModel of type T, then we can instantiate and return that class to the system. Otherwise we return “unknown model class”. In this line of code, we’re retrieving the Provider of a given type, where modelClass is the parameter of create method:

var creator: Provider<out ViewModel>? = creators[modelClass]

If our Provider map hasn’t got that specific key, we will check if there is a subclass of the ViewModel :

if (creator == null) {
for ((key, value) in creators) {
if (modelClass.isAssignableFrom(key)) {
creator = value
break
}
}
}

If we then finally find it, we can instantiate it by invoking the get method on the Provider object, and casting it to type T:

return creator.get() as T

Now that we’ve understood how this class works, another question could be where are we providing the parameters of this class ??

The answer is inside ViewModelModule. How? Always through some annotations. Let’s see them and then I’ll elaborate:

Dagger 2 can associate a Provider to a given type, and inject it into a Map. This is possible using @IntoMap on a method that provides a value with a given type, and in our case it provides a Class which extend ViewModel. The key is provided by a custom annotation @ViewModelKey , annotated using @MapKey , where we’re specifying as type of our key KClass<out ViewModel> :

So we’re injecting this object into a Map ( @IntoMap annotation) using HomeActivityViewModel.class as key, and a Provider that will build a HomeActivityViewModelobject (the parameter of the @Binds annotation) as value”. In this way, we can inject into a Dagger 2 managed object, a map of type of our famous Map<Class<? extends ViewModel>, Provider<ViewModel>> .

Conclusion

In this article I’ve tried to explain how we can handle the dependencies using some recent annotations introduced by Dagger 2 and how can we reduce the boilerplate code. In the next one, I’ll show you why this way of structuring the dependencies is so useful.

Thanks for reading, if you enjoyed it, click on the applause button to help other people find it.

Feel free to get in touch with me on Linkedin.

--

--

Matteo Pasotti

Software Engineer @Spotify, Podcaster, Moving between countries