Dependency Injection in Android with Dagger 2

Google drives developers to produce scalable and modular code with Android Architecture components. Dagger 2 takes it much further, creating a single repository where all your classes are initialized so that every Activity, Fragment, ViewModel and Repository is free from initializing the classes they are dependent on.

Dagger 2 uses Dependency Injection to create scalable, modular and easy to test code. You can say Dagger 2 Automates dependency injection, if you want to know what that means, check Manual Dependency Injection.

What is Dependency Injection?

Dependency Injection is a concept in which all the dependencies of a class are Injected into the class by either constructor, method or variables.

Okay, this post will make it easier for you.

So what is the problem with the original method?

Let’s say you have your ViewModel class and if you are making Network or Database calls, then you must be using a Repository.

You might be using one of the two methods:

  • Initializing repository in ViewModel class
  • Creating a Singleton class for the Repository
  1. You have to make changes to the ViewModel class, whenever the repository constructor changes
  2. If you are performing Unit test on View Model, it will be difficult for you to mock the repository
  3. You are violating Single responsibility principle, because now your ViewModel is initializing the Repository class.

How Does Dagger 2 help me?

When using Dagger 2, you create classes called Modules. 

Each of these Modules contains methods through which we tell Dagger how to initialize the dependencies which it will further inject. To specify a class as a Module you need to add the @Module annotation to it.

Below is an example of a Module class which initializes a Retrofit Service

@Module
class NetworkModule {
       @Singleton
       @Provides
       fun provideApiService(): ApiService {
               return Retrofit.Builder()
                     .baseUrl("http://example.com/")
                     .addCallAdapterFactory(CoroutineCallAdapterFactory())
                     .build()
                     .create(ApiService::class.java)
         }
}

You can see that we are creating a method called provideApiService() and returning our ApiService instance from it. @Provides annotation tells Dagger that the method provides a dependency.

Now, let’s see how we use that in our repository class. 

class Repository @Inject constructor(var apiService: ApiService) {
fun getUser() = apiService.getUser()
}

That’s it! We can now use the apiService in our repository anywhere. You might be wondering, how is this different from a regular constructor?

Let me tell you this, we never initialized the Repository class in ViewModel (Not the ViewModel from Jetpack) and never told Dagger how to initialize it in one of the Modules.

class UserViewModel @Inject constructor(var repository: Repository) {
fun getUser() = repository.getUser()
}

So how did Dagger know how to construct it? When we Inject Repository in ViewModel. Dagger looks at the constructor of Repository class (which is marked with an Inject annotation). The only dependency which it needs is the ApiService class which we have provided in the Network Module and thus, Dagger constructs repository for the ViewModel.

Let’s see what else we can do, we don’t want multiple instances of service in our app, let’s say we want to use the same one for UserRepository and ItemRepository. We can do that by adding an annotation @Singleton to the method providing the instance.

@Module
class NetworkModule {
      @Singleton
      @Provides
      fun provideApiService() : ApiService{
          ...
      }
}

Dagger2 and Testing

You still might be wondering, why not just use a Singleton class for each of dependencies instead of going through all the effort to setup Dagger 2 with your application.

If you look at the below code carefully, you can see that Dagger is kind of driving you to inject dependencies through the constructor.

class UserViewModel @Inject constructor(var repository: Repository) {
fun getUser() = repository.getUser()
}

In one of the older method, to use a singleton class you need to call the static method name of which may vary, to get the Single Instance. Let’s look at the Java version of a Singleton for simplicity,

class Repository {
       private static Repository instance; 
       static Repository getInstance() {
               if(instance == null) {
                   instance = new Repository();
               }
               return instance;
       }
}

Let’s see what kind of problems does it impose when we use it in our ViewModel class,

class ViewModel {
       private fun getUser() {
          return Repository.getInstance().getUser()
       }

       private fun getUserAdress() {
          return Repository.getInstance().getUserAddress()
       }
}

Now, if you try to use Mockito for writing Unit test, you will observe that it cannot work with static methods and thus cannot mock Repository class.

Even though you can use PowerMock, it is recommended that you should not.

Injecting Singletons with Dagger 2 happens at the constructor and thus, if you want to mock Singleton from Dagger 2 you can just create a TestModule and create a provides function which returns a mock.

CONCLUSIOn

I have been using Dagger 2 in almost every application and would say that it has not only made testing easy but made the code much more modular and reusable. There are several ways to implement Dagger 2, the current best way to do it would be through the AndroidInjectorModule which automates the process and eliminates a lot of boiler plate code than old ways.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.