Dagger2 dependency injection

This post demonstrates how to implement Dependency Injection in Android using Dagger 2 dependency injection framework.

Dagger 2:

Dagger 2 is a dependency injection framework for Android and Java. Originally created by Square, it is now being maintained by Google.

This framework uses compile-time code generation approach which makes Dagger 2 faster than frameworks that rely on reflection at runtime. The idea behind this technique is to execute a special pre-processor before normal code compilation in order to generate the classes that will perform the actual injection at runtime.

Dependency Injection:

If you haven’t already, please make sure to read the post about Dependency Injection in Android before proceeding with Dagger 2 tutorial. That article discusses Dependency Injection architectural pattern in general, and also lists best practices that should be followed when implementing Dependency Injection in Android.

For the rest of this post I will assume that you read the aforementioned article about Dependency Injection and understand terminology borrowed from there. I will also omit some features of Dagger 2 from our discussion here that are not in line with the listed best practices.

Gradle setup:

In order to use Dagger 2 in Android project, perform the following modifications in Gradle build files.

Declare apt plugin as a dependency in application’s root build.gradle file:

buildscript {
    repositories {
        ...
    }
    dependencies {
        ...
        classpath 'com.neenbedankt.gradle.plugins:android-apt:1.4'
    }
}

Apply apt plugin and declare additional dependencies in project module’s build.gradle file:

apply plugin: 'com.android.application'
apply plugin: 'android-apt'

android {
    ...
}

dependencies {
    ...
    apt 'com.google.dagger:dagger-compiler:2.0'
    compile 'com.google.dagger:dagger:2.0'
    provided 'javax.annotation:jsr250-api:1.0'
}

Rebuild the project.

Dependency Injection using Dagger 2:

In order to inject services into clients using Dagger 2, three conditions must be satisfied:

  1. Client must expose a field of type service as package protected or public.
  2. The exposed field must be annotated with @Inject annotation.
  3. Method inject(client) of a properly configured Dagger 2 “component” must be called, and the instance of a client must be passed as its parameter.

For example, if MyApplication is the client and SettingsManager is the injected service, it could look similar to this:

public class MyApplication extends Application {

    @Inject SettingsManager mSettingsManager; // package protected field which is injectable by Dagger 2

    private ApplicationComponent mApplicationComponent;
 
    @Override
    public void onCreate() {
        getApplicationComponent().inject(this); // injection using Dagger 2
        mSettingsManager.doSomething();
    }
 
    public ApplicationComponent getApplicationComponent() {
        if (mApplicationComponent == null) {
            mApplicationComponent = DaggerApplicationComponent.builder()
                .applicationModule(new ApplicationModule(this))
                .build();
        }
        return mApplicationComponent;
    }    
}

Conditions 1 and 2 from the above list should be easy to understand: fields must be package protected or public because Dagger 2 can’t inject private fields (no runtime reflection); @Inject annotations must be added because the pre-processor searches for these annotations in order to find the fields that must be injected. Both these conditions apply to classes in Functional set.

Condition 3, on the other hand, is not that straightforward: the concept of “component” is new and comes from Dagger 2 itself, and it is not clear what “properly configured” means in context of those “components”. If you review this condition after reading to the end of this post, you’ll notice that, in essence, this condition is equivalent to having a Construction set properly implemented according to the template provided by Dagger 2.

It is interesting to note that conditions 1 and 2 are simple and apply to Functional set, whereas condition 3 is complicated and apply to Construction set. This is yet another manifestation of Separation of Concerns principle – the framework “guides” us towards removal of “construction” logic from Functional set.

The rest of this article deals with this last complicated condition that must be satisfied in order to use Dagger 2 for Dependency Injection in Android.

Construction set implementation:

Like many other dependency injection frameworks, Dagger 2 provides a “template” that makes the implementation of the Construction set easier. This template requires us to implement Construction set using three “types” of classes:

  • Module
  • Component
  • Scope

Let’s review each of the types individually.

Module:

Module (in context of Dagger 2) is a class that resolves dependencies between services and instantiates them. If you’re familiar with the term “objects graph”, then module encapsulates the declaration of the objects graph.

In order for a class to be recognized as a module by Dagger 2, it should be annotated with @Module annotation.

Example of a module that declares SharedPreferences object as an injectable service:

@Module
public class ApplicationModule {

    private final Application mApplication;

    public ApplicationModule(Application application) {
        mApplication = application;
    }

    @Provides
    SharedPreferences provideSharedPreferences() {
        return mApplication.getSharedPreferences(Constants.PREFERENCES_FILE, Context.MODE_PRIVATE);
    }

}

Note the following:

  1. Module is just a regular class annotated with @Module annotation.
  2. Module can optionally take in constructor arguments. These arguments are the “fundamental” dependencies of other services declared in the module. Context and its subclasses are the most common types of module’s constructor arguments.
  3. Each injectable service declared in the module is being returned by one module’s method. That method must be annotated with @Provides annotation. The name of the method is not important.

The current implementation of ApplicationModule provides just SharedPreferences. What if we need this module to also provide SettingsManager which depends on SharedPreferences?

According to point #3 above, this should require addition of a single annotated method that returns SettingsManager:

@Module
public class ApplicationModule {

    private final Application mApplication;

    public ApplicationModule(Application application) {
        mApplication = application;
    }

    @Provides
    SharedPreferences provideSharedPreferences() {
        return mApplication.getSharedPreferences(Constants.PREFERENCES_FILE, Context.MODE_PRIVATE);
    }

    @Provides
    SettingsManager provideSettingsManager(SharedPreferences sharedPreferences) {
        return new SettingsManager(sharedPreferences);
    }

}

Note how this new method takes in the dependencies required by SettingsManager as arguments.

With respect to what we saw until now, you probably have (at least) two questions:

  1. Who calls module’s methods annotated with @Provides annotation?
  2. There is no visible connection between SharedPreferences returned by provideSharedPreferences method and the argument of provideSettingsManager method. Shouldn’t we “wire” these methods together?

As we said earlier, Dagger 2 uses a pre-processor that generates code. This generated code will call methods annotated with @Provides annotation when we perform the actual injection using the framework (will be discussed shortly). Pre-processor also ensures that for every dependency declared as an argument of any @Provides annotated method, there is a method that actually provides that dependency. The generated code takes care of “wiring” of all the @Provides annotated methods together.

By taking care of all the “wiring” activities, Dagger 2 greatly reduces the amount of boiler-plate code that we need to write.

Component:

Component (in context of Dagger 2) is a class that performs the actual injection into clients (“injector”). If you are familiar with the term “dependency injection container”, then component is one of those.

We don’t define components. Framework’s pre-processor generates them. But we do need to “tell” the pre-processor what functionality the generated component should have. This is done through declaration of interfaces annotated with @Component annotation.

Although the interface that we declare and the actual class generated by the pre-processor are different entities, both of them are usually called components. In the rest of this post I will refer to both the interface and the actual class as components. Which exact entity is being implied can be understood from the context.

Example of declaration of a component that can inject services provided by ApplicationModule into MyApplication:

@Component(modules = {ApplicationModule.class})
public interface ApplicationComponent {

    void inject(MyApplication myApplication);

}

Note the following:

  1. Unlike other annotations we’ve seen so far, @Component annotation takes in modules “element” (argument). This element tells the pre-processor which modules will provide services injected by this component.
  2. Each client that the component can inject into is being declared as an argument of a single void method. The name of the method is not important, but it is a common convention to call them inject.

Currently, this component can inject into MyApplication only. What if we want to inject using the framework into MyActivity as well?

According to #2 above, this requires addition of a single void method that takes in MyActivity as an argument:

@Component(modules = {ApplicationModule.class})
public interface ApplicationComponent {

    void inject(MyApplication myApplication);

    void inject(MyActivity myActivity);

}

If we build the project now, the framework’s pre-processor will scan MyApplication and MyActivity classes for fields annotated with @Inject annotation. Then the pre-processor will validate that for each such field there is a method in ApplicationModule that provides the required service. In case of a successful validation, the pre-processor will generate an implementation of ApplicationComponent interface that can be used for injection into MyApplication and MyActivity.

The auto-generated component’s name will be Dagger string followed by the name of the component interface. In this case the name will be DaggerApplicationComponent.

Component usage:

Now, when we know what components are, we can once again review MyApplication class from the beginning of the post, but this time actually understand what’s going on there:

public class MyApplication extends Application {

    @Inject SettingsManager mSettingsManager; // package protected field which is injectable by Dagger 2

    private ApplicationComponent mApplicationComponent;
 
    @Override
    public void onCreate() {
        getApplicationComponent().inject(this); // injection using Dagger 2
        mSettingsManager.doSomething();
    }
 
    public ApplicationComponent getApplicationComponent() {
        if (mApplicationComponent == null) {
            mApplicationComponent = DaggerApplicationComponent.builder()
                .applicationModule(new ApplicationModule(this))
                .build();
        }
        return mApplicationComponent;
    }    
}

Note the following:

  • ApplicationComponent interface was defined by us.
  • DaggerApplicationComponent class, that implements ApplicationComponent interface, was generated by the framework’s pre-processor.
  • Objects of type DaggerApplicationComponent are instantiated using Builder design pattern.
  • We must manually instantiate the ApplicationModule and pass it to DaggerApplicationComponent Builder.

Before proceeding any further, it is very important that you understand what each line in MyApplication class does. I encourage you to make sure that you have a clear picture in mind of how the code in MyApplication depends on and integrates with the code in ApplicationComponent interface and ApplicationModule class.

Have you asked yourself why, in the above example, ApplicationComponent is being assigned to a field, and why getApplicationComponent method is public? Both these implementation choices make no sense since the component is being used just once inside the client.

These implementation choices don’t make sense only as long as the component is used for injection into a single client. If we want to inject in another client using the same component, this code will immediately become reasonable.

Let’s say that we also want to use ApplicationComponent for injection into MyActivity. Client’s code will look like this then:

public class MyActivity extends Activity {

    @Inject SettingsManager mSettingsManager; 
 
    @Override
    public void onCreate() {
        ((MyApplication)getApplicationComponent()).inject(this); 
    }

    @Override
    public void onResume() {
        mSettingsManager.doSomething();
    }
   
}

Note the following:

  • In order for all the clients to use the same instance of ApplicationComponent, it must be stored in a field in MyApplication class.
  • In order for MyActivity to get access to the instance of ApplicationComponent, the method getApplicationComponent() must be public.

It might not be evident at this point why it is important to share the same instance of ApplicationComponent among all the clients that use it. The following discussion of Scope will provide the explanation.

Scope:

Scope (in context of Dagger 2) is an annotation that can be used in order to change the lifecycle of the injected services.

I have already written a very detailed post about scopes: Android Dagger 2 Scopes Demystified. Therefore, I will not go into lengthy explanations here (you can read the aforementioned post), but just re-state the main characteristics:

  1. Any time a non-scoped service is being injected by the same component, a new instance of a service is created.
  2. First time a @Singleton scoped service is being injected, a new instance of a service is created and cached inside the injecting component, and the same exact instance will be used upon injection of other fields of the same type by the same component.
  3. Custom user-defined scopes are functionally equivalent to a predefined @Singleton scope.
  4. Injection of scoped services is thread safe in context of safe-initialization and safe-publication of objects.

Up until now we did not use scopes at all. Therefore, according to characteristic #1, every injection of SettingsManager in the previous examples provided a newly instantiated service to the clients.

Scope usage:

Let’s imagine now that the implementation of SettingsManager includes a very sophisticated in-memory cache.

Since the cache is in-memory, we must ensure that all the clients that depend on SettingsManager have a reference to the same instance. Otherwise, if different clients reference different instances, changes made by one client might not be visible by another client, which can lead to a system entering an undefined and inconsistent state.

The immediate consequence of the above requirement is that SettingsManager must become “global” object. Global here means that the instance of an object must persist as long as the application’s process is alive. In Android, there are two types of global objects:

  1. Objects referenced by static fields
  2. Objects referenced by Application object (might be referenced through a chain of other objects).

Global objects of type #1 are being used in Singleton anti-pattern. This type of global objects should be avoided at all costs.

Global objects of type #2, on the other hand, are necessary and useful. Application by itself is a global object of type #2.

So, we’d like to make SettingsManager a global object of type #2. In order to do this, we need to have a chain of hard references from MyApplication to it. Since this object is being injected by DaggerApplicationComponent, the first reference will be from MyApplication to DaggerApplicationComponent. This explains why we assigned DaggerApplicationComponent to a field inside MyApplication class.

DaggerApplicationComponent, in turn, references ApplicationModule internally. Therefore, we can achieve our goal by referencing an instance of SettingsManager from ApplicationModule, and always providing this single instance for injection.

The following modification to ApplicationModule achieves what we need:

@Module
public class ApplicationModule {

    private final Application mApplication;

    private SettingsManager mSettingsManager;

    public ApplicationModule(Application application) {
        mApplication = application;
    }

    @Provides
    SharedPreferences provideSharedPreferences() {
        return mApplication.getSharedPreferences(Constants.PREFERENCES_FILE, Context.MODE_PRIVATE);
    }

    @Provides
    SettingsManager provideSettingsManager(SharedPreferences sharedPreferences) {
        if (mSettingsManager == null) {
            mSettingsManager = new SettingsManager(sharedPreferences);
        }
        return mSettingsManager;
    }

}

The above code turns SettingsManager into a global object and ensures that all clients are being injected with exactly the same instance. However, note how much uglier the implementation of ApplicationModule became.

It turns out that Dagger 2 provides its own built-in mechanism that allows for “globalization” of injected services. This mechanism is based on scopes.

According to characteristic #2 from the post Android Dagger 2 Scopes Demystified, “globalization” of SettingsManager can be achieved with the following modification of ApplicationModule:

@Module
public class ApplicationModule {

    private final Application mApplication;

    public ApplicationModule(Application application) {
        mApplication = application;
    }

    @Provides
    SharedPreferences provideSharedPreferences() {
        return mApplication.getSharedPreferences(Constants.PREFERENCES_FILE, Context.MODE_PRIVATE);
    }

    @Provides
    @Singleton
    SettingsManager provideSettingsManager(SharedPreferences sharedPreferences) {
        return new SettingsManager(sharedPreferences);
    }

}

Note that the only difference from the original implementation is addition of @Singleton annotation to the method that provides the SettingsManager. Single annotation approach is much simpler solution than all the code we had to write ourselves. In addition, according to characteristic #4, this approach is even better because it makes this “globalization” of SettingsManager thread-safe (though I haven’t seen yet a valid use case for injection from any thread other than main).

One restriction enforced by framework’s pre-processor is that if a component injects scoped services, then this component must be scoped as well. The scope of the component must be identical to the scope of the services.

Therefore, in order for the code to compile, we must also add scope to ApplicationComponent:

@Singleton
@Component(modules = {ApplicationModule.class})
public interface ApplicationComponent {

    void inject(MyApplication myApplication);

    void inject(MyActivity myActivity);

}

Conclusion:

In this post we saw how to implement basic Dependency Injection architectural pattern using Dagger 2 dependency injection framework.

Why I say that Dependency Injection implemented following this tutorial will be “basic”? Because although the material provided here is sufficient for implementation of Dependency Injection, that implementation will probably not be optimal in terms of maintainability.

In my opinion, optimal implementation of Dependency Injection with Dagger 2 in Android must use the following features which were not covered in this post:

  • Multi-module components
  • Subcomponents
  • Custom scopes

If there will be demand, I will write another post discussing these advanced topics.

The good news are that there is open-source application that I wrote which implements a proper Dependency Injection. That application is using Dagger 2, including all of the aforementioned features. So, if you’re up to some source code exploration – take a look at IDoCare’s source code. The relevant configuration is in dependencyinjection package.

That will be all for this post. Thank you for reading.

Please leave your comments and questions below, and consider subscribing to our newsletter if you liked the post.

This article has 6 comments

  1. Jovche Reply

    Hello!

    I really like this post and as well as the rest of the posts in this blog, it is a very nice read and very well explained so it’s easy to understand.
    I can’t agree more with the following statement: “Global objects of type #1 are being used in Singleton anti-pattern. This type of global objects should be avoided at all costs.” – and indeed I am trying to avoid statics as much as possible.

    I am not very experienced with Dagger, but I am trying to get more involved. Here is a situation I have in my current project that I want to migrate under dagger, but I find it quite tricky:

    We have a configuration map that comes from server when the user launches the application. It’s basic key-value map. So we store that map into singleton class that is kinda wrapper that provides the values. We need those config values almost everywhere in the app (imagine we have a feature that is enabled/disabled based on users county).

    Here is roughly how the code looks like:

    public class AppConfiguration {
    
        private static AppConfiguration instance;
    
        private AppConfiguration() { }
    
        public static AppConfiguration instance() {
            if (instance == null) {
                instance = new AppConfiguration();
            }
            return instance;
        }
    
        private Map configuration = new HashMap();
    
        public void update(Map configuration) {
            if (configuration != null) {
                this.configuration = configuration;
            }
        }
    
        public String getConfigValue() {
            return configuration.containsKey("configValue") ? configuration.get("configValue") : "";
        }
    
        ....
    }
    

    I understand that I can make this wrapper class a singleton, and then inject it wherever I need it, but what is not clear to me is how do I initialise it with the configuration map that comes from the server, or maybe whole this concept is simply wrong and I have to come up with something better.

    I would really like to hear what do you think and what would be your approach for solving this issue.

    Thanks a lot in advance.

    • Vasiliy Reply

      Hello and thank you for kind words.
      What you suggested looks like a good solution: make AppConfiguration global and inject it using Dagger. Search for the classes that currently call instance() method and directly inject AppConfiguration into them. Then remove static calls. If these classes are not top level (as defined in this article), then you’ll need to propagate AppConfiguration from some top level component into these classes using Constructor Injection.

  2. Jack Reply

    Hi Vasiliy. Thanks for writing these great posts on Dagger and Dagger Scope. These are the most helpful posts I’ve read online. Do you have plan writing some advanced topics about Dagger 2? Can’t wait to see that.

    • Vasiliy Reply

      Hi Jack. Thanks for your feedback!
      I will probably write another post about Dagger 2. This post will show how to structure components and modules in a readable and maintainable way, and warn against usage of some Dagger 2 features that I find counter-productive. But if you want to know how I would approach components and modules structuring, you can take a look at the source code of IDoCare application (there is a post about it). I think that the implementation of Dependency Injection in this app is very decent.

  3. Uval Reply

    Hi, thanks for the really nice tutorial!
    A side question – why do you consider using static objects an anti pattern for singleton?
    It’s practically the same as holding a member inside Application, but without the fuss & overhead. As long as the developer know what he’s doing (not holding a static reference to activity scoped Context and its derivatives) – I’m not aware of any problem (and even Dianne Hackborn suggested this approach). Can you elaborate?

    • Vasiliy Reply

      Hello Uval. Thanks for your question.
      I do consider using static objects in general, and Singleton pattern specifically, to be counter productive and, in some cases, even dangerous. And it has nothing to do with memory leaks.

      For thorough explanation of why Singletons are bad you can read this post by Misko Hevery (I couldn’t summarize it better myself).
      Furthermore, Misko argues in another post that all static methods should be avoided altogether.
      If you need to hear an opinion of someone more authoritative than Misko in order to outweigh Dianne’s suggestion, read this post by Robert Martin (aka Uncle Bob).
      And the last source I would like to mention is Steve Mcconnell’s all-time software engineering classics: Code Complete. Section 13.3 of this book is titled “Global Data” and it discusses various problems that might arise due to use of global data. While this section is not about Singletons per se, Singletons are global data in disguise and can be seen as collections of what Steve called “access routines”. Therefore, Singletons introduce the same risks as access routines for global data. Code Complete does acknowledge that there are valid use cases for global data, but it also points out that usages of global data are usually signs of incorrect design.

      Now, some developers who use Singletons claim that it is just a matter of taste and you can use them as long as you know what you do. However, to me it looks like some kind of self deception. In my experience, singletons always become a major maintenance and testability problem.

      There are valid use cases for Singletons (e.g. if you write a library, you can expose its facade as a Singleton). But, IMHO, these are really rare and should be considered as exceptions rather than standard design approaches.

Leave a Comment

Your email address will not be published. Required fields are marked *