The lost droid and the magic Dagger: an intro to dependency injection for Android.

Definitions and advantages

You might have heard the term dependency injection before. You might have even tried to Google it and stumbled upon a definition that introduced 2 new unknown terms (inversion of control, dependency injection pattern). Personally, I find the following definition to be the most descriptive:

Dependency injection for five-year-olds:
When you go and get things out of the refrigerator for yourself, you can cause problems. You might leave the door open, you might get something Mommy or Daddy doesn’t want you to have. You might even be looking for something we don’t even have or which has expired.
What you should be doing is stating a need, "I need something to drink with lunch," and then we will make sure you have something when you sit down to eat.
- StackOverflow answer

In my own words, instead of being responsible for creating and providing all the dependencies an object needs, a magic “framework” takes this responsibility. This approach has many advantages, with the most important being:

  • Since the framework is responsible for providing the dependencies, we can easily change an implementation of an abstraction without making a lot of changes.
  • When testing, we can easily replace the real dependencies with fake/mock implementations.
  • Dependency fulfilling for complex objects (i.e. objects that depend on objects that depend on other objects, multiple levels of dependencies) becomes easy since the framework takes care to figure it out.

Dagger 2

Dagger 2 is a popular dependency injection (DI) framework for Java and especially popular in Android. The main reason is that it generates all the required code and does not use any reflection, which is slow in general but especially heavy in mobile. This generate-all-the-code approach has the advantage that any mistake regarding the dependency graph will be caught during compile-time instead of run-time. The downside is that Dagger is considered less flexible than other DI frameworks out there, but the speed and compile-time safety that offers make up for its rigidness.

Concepts

Like every framework, Dagger has its own “vocabulary” that you need to learn in order to use it. If you used another DI framework before, you will surely find familiar terms.

Module

A class annotated with @Module that specifies what and how you provide a dependency. The what is defined by the return type of a @Provides annotated method. The method name does not matter.

The following example is fairly simple. Let’s suppose that MovieDbHelper is an interface and MovieDbHelperImpl is a respective concrete implementation. Every time some part of the app requests a MovieDbHelper, the provideMovieDbHelper() will execute and provide the dependency with an implementation of the requested interface.

@Module
class ApplicationModule {

@Provides
MovieDbHelper provideMovieDbHelper() {
return new MovieDbHelperImpl();
}
}

What if the MovieDbHelperImpl needed its own dependencies in order to be instantiated? Adding the dependencies as parameters in the provideMovieDbHelper(..) will make Dagger look for another @Provides method to fulfill the dependencies.

Creating Singletons is easy with Dagger; just annotate the @Provide annotated method with @Singleton to ensure that only a single instance will be returned when requested.

@Module
class ApplicationModule {

private Context context;

ApplicationModule(Context context) {
this.context = context;
}

@Provides
Context providesContext() {
return context;
}

@Provides
@Singleton
MovieDbHelper provideMovieDbHelper(Context context) {
return new MovieDbHelperImpl(context);
}
}

Inject

A class can request its dependencies from the DI framework by annotating with @Inject its constructor, functions or member variables.

class MovieDbHelperImpl {
    @Inject
MovieDbHelperImpl(Context context) {
this.context = context;
}
    [...]
}

Annotating a constructor with @Inject makes it discoverable to Dagger. After the above example, any other class that requests a MovieDbHelperImpl will get an instance, without requiring a @Provides method in a Module.

Constructor injection works fine for your own classes that you have total control over when to instantiate them. This is not the case for many Android specific objects such as Activities, Fragments, etc that are instantiated and provided by the system. In this case, you have to use injection of functions or member variables.

class SearchMoviesActivity extends AppCompatActivity {

@Inject SearchMoviesPresenter presenter;
    [...]
}
class SearchMoviesActivity extends AppCompatActivity {

SearchMoviesPresenter presenter;

@Inject
void onInject(SearchMoviesPresenter presenter) {
this.presenter = presenter;
}
    [...]
}

Component

Components are the glue between Modules and Injection targets. In other DI frameworks, you might find them with the name Injectors, which I find a more descriptive name for their functionality.

They are interfaces that are annotated with @Component and indicate which @Module(s) are including (i.e. which dependencies they know about and they can provide) and the injection targets that they can inject (i.e. the objects that request dependencies with the @Inject annotation).

@Component(modules = {ApplicationModule.class})
interface ApplicationComponent {
    void inject(SearchMoviesActivity searchMoviesActivity);
}

In order to actually use the Component to inject your injection targets (in the example above the SearchMoviesActivity), Dagger will generate an implementation of this interface prefixed with the word “Dagger” when your project is re-build. For example, to inject the dependencies requested by SearchMoviesActivity (using the @Inject annotated constructors/functions/variables) you need to create the component and inject the activity. If you have Modules that do not have a no-arguments constructor, you need to instantiate the module before creating the component.

public class SearchMoviesActivity extends AppCompatActivity {

private ApplicationComponent applicationComponent;

@Override
public void onCreate() {
super.onCreate();

applicationComponent = DaggerApplicationComponent
.builder()
.applicationModule(
new ApplicationModule(getApplicationContext()))
.build();

applicationComponent.inject(this);
}
    [...]
}

Scopes, Qualifiers, Subcomponents and more

This post covered just the tip of the iceberg; Dagger 2 has many more features to explore. Maybe I will try to cover them in a future post.

Simple sample setup

Instead of going on and overwhelm you with even more concepts, I will try to apply what are covered until now to show how you can use DI in a simple Android app. This is not an ideal setup, it’s using only a single component for the whole app but it can work for small apps. Ideally, you would want to have multiple components/subcomponents for each independent unit in your app.

Setup Dagger

Include the following lines in your app-level gradle file (app/build.gradle).

dependencies {
compile "com.google.dagger:dagger:2.9"
annotationProcessor "com.google.dagger:dagger-compiler:2.9"
provided "javax.annotation:jsr250-api:1.0"
}

Application Module

A module that will provide the application context (and potentially other app-wide dependencies).

@Module
public class ApplicationModule {

private Application app;

public ApplicationModule(Application app) {
this.app = app;
}

@Provides
@Singleton
Context providesContext() {
return app;
}

}

Application Component

The injector that needs to have a function for all the classes that can be injected. In this simple setup, you need to have an inject function for each Activity in the app.

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

void inject(MainMenuActivity mainMenuActivity);

void inject(PlayGameActivity playGameActivity);
}

Custom application
The single component that we have, will be instantiated and be accessible from here.

public class MyApplication extends Application {

private ApplicationComponent applicationComponent;

@Override
public void onCreate() {
super.onCreate();

applicationComponent = DaggerApplicationComponent
.builder()
.applicationModule(
new ApplicationModule(getApplicationContext()))
.build();
}

public ApplicationComponent getComponent() {
return applicationComponent;
}
}

Don’t forget that you need to modify the AndroidManifest.xml to declare you custom Application class.

<application
android:name=".application.MyApplication"
[...]

Base Activity

All the activities in the app will extend this base class that will provide (a kind of) a convenient way to inject each activity. In onCreate(..), it retrieves the component and calls the abstract method inject(..) that is implemented by each Activity.

public abstract class BaseActivity extends AppCompatActivity {

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
inject(((MyApplication) getApplication()).getComponent());
}

/**
* Implement and call component.inject(this) for dependency
* injection of this Activity.
*/
protected abstract void inject(ApplicationComponent component);
}

Every activity

The activities have to call the super.onCreate(..) and implement the inject(..).

The Component needs to have an appropriate inject(..) function that takes as a parameter the current activity. Having only an injection method for the base class (e.g. inject(BaseActivity activity)) will not work.

public class MainMenuActivity extends BaseActivity {

@Inject MainMenuPresenter presenter;
    @Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
[...]
});

@Override
protected void inject(ApplicationComponent component) {
component.inject(this);
}
    [...]
}

Every Presenter or another object that can be injected

Don’t forget that for Dagger to be able to know (and inject) an object, the constructor must be annotated with @Inject or it must be included in a Module.

public class MainMenuPresenter {

@Inject
MainMenuPresenter() {
[...]
}
}

Conclusion

This was just a taste of what dependency injection looks like and how it can be used in a very very simple Android app using Dagger 2. If you want to use it in a more complex app you should dig a little more and learn concepts such as Scopes and Subcomponents before using it in your app. Dagger 2 is a really powerful, efficient, and actively developed DI framework that I enjoyed working with. I hope you will enjoy learning and working with it as well!