Dagger 2 Tutorial

Dagger 2 is a framework which became the de-facto standard for implementation of dependency injection in Android.

Since the original Dagger 1 framework is now obsolete (to the best of my knowledge), Dagger 2 is being widely referred to as just Dagger. So, whenever you see Dagger today, you can safely assume that it refers to Dagger 2. I will use both Dagger and Dagger 2 interchangeably in this article.

Dagger was originally created by Square and is now being maintained by Google.

In this post I’m going to show you how to use Dagger 2 to implement a basic dependency injection in Android.

Dependency Injection:

One common misconception among software developers in general, and Android developers specifically, is that dependency injection framework is a synonym of dependency injection. This is very far from being correct.

Dependency injection frameworks are just tools that assist in implementation of Dependency Injection architectural pattern. Dagger is not an exception.

Over the past years I explained dependency injection to many Android developers. In my experience, developers who understand what dependency injection is prior to learning about specifics of dependency injection frameworks acquire a much deeper insights and learn faster.

Therefore, I strongly advice you to read the post about Dependency Injection in Android before proceeding with this tutorial. This 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 and understand the terminology borrowed from there.

Gradle setup:

To use Dagger 2 in Android project I need to add the following dependencies in module’s build.gradle file:

dependencies {
    ...
    implementation 'com.google.dagger:dagger:2.14.1'
    annotationProcessor 'com.google.dagger:dagger-compiler:2.14.1'
     ...
}

After making this modification Android Studio will show a popup suggesting to sync the project. Go ahead and click on “sync now”.

Dependency Injection using Dagger 2:

To inject services into clients with Dagger three conditions must be satisfied:

  1. Client must expose a field to be injected 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, with client’s reference as its argument.

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; // injectable field

    private ApplicationComponent mApplicationComponent;

    @Override
    public void onCreate() {
        getApplicationComponent().inject(this); // ApplicationComponent injects into "this" client
        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 doesn’t use runtime reflection and can’t inject private fields . @Inject annotations must be added because Dagger’s pre-processor searches for these annotations in order to find the fields that must be injected.

Condition 3, on the other hand, is not that straightforward.

The concept of “component” is new and comes from Dagger 2 itself. In addition, it is probably not clear what “properly configured” means in context of “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 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. You will declare which services Dagger should know about and what is the relationship between them in one or more modules.

If you’re familiar with the term “object graph”, then modules encapsulate the declaration of the object 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 “bootstrapping” 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 is returned by one module’s method that is annotated with @Provides annotation. The name of this “provider” 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?

Remember that I said that Dagger 2 uses a pre-processor that generates code? It is the generated code that takes care of the above concerns.

During project build pre-processor scans the files and generates code which calls methods annotated with @Provides annotation. It also ensures that for every dependency declared as an argument of any @Provides annotated method, there is a method that actually provides that dependency. Finally, the generated code takes care of “wiring” of all the @Provides annotated methods together.

By taking care of all the “wiring” activities, Dagger greatly reduces the amount of boiler-plate code that you will 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.

You don’t need to implement components yourself – framework’s pre-processor generates them. But you 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. 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. 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 I 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.

After scanning of clients’ declarations and ensuring that all required services are indeed provided, pre-processor will generate 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 you know what components are, we can once again review MyApplication class from the beginning of the post. This time you will have an easier time understanding what’s going on there:

public class MyApplication extends Application {

    @Inject SettingsManager mSettingsManager; // injectable field

    private ApplicationComponent mApplicationComponent;

    @Override
    public void onCreate() {
        getApplicationComponent().inject(this); // ApplicationComponent injects into "this" client
        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. Take your time and build a clear picture in mind of what’s going on. Specifically, understand how the code in MyApplication relates 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 seem to make no sense because ApplicationComponent is being used just once.

Well, this indeed makes no sense as long as the component is used for injection into a single client. However, once you’ll need to inject services into multiple clients using ApplicationComponent, this code will immediately become reasonable.

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

public class MyActivity extends Activity {

    @Inject SettingsManager mSettingsManager;

    @Override
    public void onCreate() {
        ((MyApplication)getApplication()).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 would you want to use the same instance of ApplicationComponent in all clients. The following discussion of Scopes will explain why it is important.

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, therefore I will not go into lengthy explanations here. Let me just re-state the main Scope’s 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. 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.

Since I did not use scopes in this tutorial until now, according to characteristic #1 all clients got different instances of SettingsManager in the previous examples.

Let’s change this with Scopes.

Scope usage:

Imagine now that the implementation of SettingsManager includes a very sophisticated in-memory cache.

Since the cache is in-memory, I 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 will not be visible by another client which will 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.

To make the in-memory cache inside SettingsManager shared across all clients I need to turn SettingsManager into a global object of type 2. To achieve this I 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 I assigned DaggerApplicationComponent to a field inside MyApplication class.

Dagger ensures that DaggerApplicationComponent references ApplicationModule internally. That will be a second reference in the chain.

Looks like I can achieve the goal of turning SettingsManager into a global object if I can somehow make ApplicationModule store a reference to it, and make sure that ApplicationModule will later always provide this referenced instance for injection.

The following modification to ApplicationModule achieves achieves exactly what I 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, I don’t really need to write all this code myself. 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 aforementioned post about scopes, “globalization” of SettingsManager can also 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 and cleaner solution than all the code I had to write myself.

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

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

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

    void inject(MyApplication myApplication);

    void inject(MyActivity myActivity);

}

Now all the clients which depend on SettingsManager will be injected with exactly the same global instance.

Conclusion:

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

Why “basic Dependency Injection architectural pattern”? 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

You can learn how to use these features in my Advanced Dagger 2 video Tutorial.

In addition, you might like my recent course about Dependency Injection in Android with Dagger 2.

That will be all for now. Thank you for reading.

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

Check out my premium

Android Development Courses

11 comments on "Dagger 2 Tutorial"

  1. 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:

    [code language=”java”]
    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") : "";
    }

    ….
    }
    [/code]

    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.

    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.

      Reply
  2. 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.

    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.

      Reply
  3. 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?

    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.

      Reply
  4. Best dependency injection article.
    But i have the following query with regards to MVP implementation with Dagger2 .
    If we provide all the required presenters in a single Controller Module and then use that module in every controller, it may happen that the controller module provides all dependencies which may be of no use in some Activity/Fragment. For example suppose there are 2 fragments. Fragment A and Fragment B. Fragment A requires Presenter A and Fragment B requires Presenter B. The controller module provides both these Presenters. Then even if Fragment A doesnot need Presenter B, it will be injected in Fragment A, which is not appropriate according to me(I may be wrong). So, it would be great if you can clarify it

    Reply
    • Hi Smit,
      Thanks for your feedback.
      If Fragment A doesn’t need Presenter B, then it will not declare it as @Inject annotated field. Therefore, Presenter B will not be injected into Fragment A.
      The beauty of having one component (with potentially multiple modules) that is used for injection into all Fragments and Activities is that if Fragment A will need Presenter B in the future, then it will be able to get it with a single line of code. No modifications to DI structure (construction set) will be required. [maybe presenter is not the best example – you can think of any other object which is used in Fragment B, but is not currently used in Fragment A]

      Reply

Leave a Comment