· 6 min read

Dagger: dependency injection on Android (Part 2)

If you read first post about dependency injection, you will probably be looking for some real code. There are some beautiful examples about coffee makers at Dagger page, and an awesome model project by Jake Wharton for more experienced users. But we need something easier and coffee is not our main business model, so this article will provide an example where we are injecting some simple components to let us understand the basics.

Source code explained here can be found at DaggerExample repository at Github.

Include Dagger into your project

There are two libraries that must be added if you want to use Dagger:

dependencies { 
    compile fileTree(dir: 'libs', include: ['*.jar']) 
    compile 'com.squareup.dagger:dagger:1.2.+' 
    provided 'com.squareup.dagger:dagger-compiler:1.2.+' 
}

First one is Dagger library. Second library is the dagger compiler. It will create required classes in order to be able to inject dependencies. That’s the way it can avoid most reflection, by creating precompiled classes. As we only need it to compile the project, and won’t be used by application, we mark it as provided so that it isn’t included in final apk.

Creating your first module

Modules will be your daily job with dagger, so you need to feel comfortable with them. Modules are classes that provide instances of the objects we will need to inject. They are defined by annotating the class with @Module. There are some extra parameters that may be configured, but I’ll explain when we use them.

Create a class called AppModule, that will provide, for example, the Application Context. It’s usually interesting to have an easy access to it. I created App, which extends from Application, and added to the manifest.

@Module(injects = { App.class }) 
public class AppModule {
    
    private App app;

    public AppModule(App app) { 
        this.app = app; 
    }

    @Provides 
    @Singleton 
    public Context provideApplicationContext() { 
        return app; 
    } 
}

What is new here?

@Module : identify this class as a Dagger module. injects : Classes where this module is going to inject any of its dependencies. We need to specify those classes that are directly injected into object graph. This will be covered soon. @Provides: Identify method as an injection provider. The name of the method doesn’t matter, it only relies on what class type is provided. @Singleton : if it’s present, the method will return always the same instance of the object, which is far better than regular singletons. If not, every time this type is injected, we’ll get a new instance. In this case, as we are not creating a new instance, but returning an existing one, it would be the same if we don’t annotate as singleton, but it explains better what the provider is doing. Application instance is unique.

Why regular singletons are evil

Singletons are probably the most dangerous dependencies a project can have. First of all, because due to the fact that we are not creating an instance, it’s really hard to know where we are using it, so these are “hidden dependencies”. On the other way, we have no way to mock them for testing or to substitute it with another module, so our code becomes hard to maintain, to test and to evolve. Injected singletons, on the other way, have the benefits of a singleton (a unique instance) and, as we can create new instances at any moment, it’s easier to mock and to substitute with another piece of code, by subclassing or making them implement a common interface.

We will be creating another module in a new package called domain. It’s quite useful to have (at least) a module in every architecture layer. This module will provide an analytics manager, that will throw an event when the app starts, only by showing a Toast. In a real project, this manager could call any analytics service such as Google Analytics.

@Module(complete = false, library = true) 
public class DomainModule {

    @Provides 
    @Singleton 
    public AnalyticsManager provideAnalyticsManager(Application app){ 
        return new AnalyticsManager(app); 
    }
}

By identifying this module as not complete, we say that some of the dependencies in this module need to be provided by another module. That’s the case of Application, which comes from AppModule. When we require this AnalyticsManager from a dependency injection, dagger will use this method, and will detect that it needs another dependency, Application, which will be requested to the object graph (almost there!). We also need to specify this module as a library, because dagger compiler will detect that AnalyticsManager is not being used by itself or its injected classes. It’s acting as a library module for AppModule.

We will specify that AppModule will include this one, so back to previous class:

@Module(injects = { App.class }, includes = { DomainModule.class }) 
public class AppModule { 
    ... 
}

includes attribute is there for that purpose.

Creating the Object Graph

The object graph is the place where all these dependencies live. The object graph contains the created instances and is able to inject them to the objects we add to it.

In previous examples (AnalyticsManager) we have seen the “classic” dependency injection, where injections are passed via constructor. But we have some classes in Android (Application, Activity) where we don’t have control over constructor, so we simply need another way to inject its dependencies.

The combination of ObjectGraph creation and this direct injection is represented in App class. The main object graph is created in the Application class and it is injected in order to get its dependencies.

public class App extends Application {

    private ObjectGraph objectGraph; 
    @Inject 
    AnalyticsManager analyticsManager;

    @Override 
    public void onCreate() { 
        super.onCreate(); 
        objectGraph = ObjectGraph.create(getModules().toArray()); 
        objectGraph.inject(this); 
        analyticsManager.registerAppEnter(); 
    }

    private List<Object> getModules() { 
        return Arrays.<Object>asList(new AppModule(this)); 
    } 
}

We specify dependencies by annotating them with @Inject. These fields must be public or default scoped, so that dagger can assign them. We create an array with our modules (we have only one, DomainModule is included in AppModule), and create an ObjectGraph with it. After it, we inject App instance manually. After that call, dependencies are injected, so we can call AnalyticsManager method.

Conclusion

Now you know the basics about Dagger. ObjectGraph and Modules are the most interesting components that must be mastered to use Dagger efficiently. There are some more tools such as lazy injections or provider injections that are explained at Dagger site, but I don’t recommend dive into them until you are fluent using what we saw here.

Don’t forget that source code is available at Github.

Next (and probably last) post about Dagger will be focused on scoped object graphs. It basically consists of creating new object graphs that live only where its creator does. It’s common to create scoped graphs for activities.

Stay tuned via RSS or my Google+ profile.

    Share:
    Back to Blog