Dagger 2 Framework Highlights

Dagger 2 is a library for the Android projects implementing the Dependency Injection template. There are multiple articles on how to set up Dagger 2 so I’ll be giving my thoughts on why this library matters and how it helps.

The Brief History of Dagger 2

Dagger 1 was created by the company called Square (who also came up with Retrofit, Picasso, okhttp, and a bunch of other popular Android libraries). After that, Google developers enhanced and perfected Dagger 1 (for example, deprecated reflection) giving it the new name of Dagger 2.  I’m specifically interested in Dagger 2 and I’m going to write about it referring to it as just “Dagger”.

The Narrative

Inversion of Control is an important principle of object-oriented programming, allowing developers to reduce the system components coupling (relationship strength). This principle is often jokingly called the “Hollywood Principle – Don’t call us, we’ll call you”.

Dependency Injection is one of the IoC implementations that apply to the dependencies management. Besides this, there are templates like Factory or service Locator).

The 99 Problems

Let’s take a simple example: every Android project has one or many Singletons. Considering the transitivity of Activity and Fragment lifecycles, you can barely get along without any Singletons. Usually, they call this Singleton “the blah-blah manager” for example,   ApplicationManager, DataManager or whatever. This manager usually stores the API for server access, data access object (DAO), or the session to access the local database, and a lot of other stuff to grant Activity and Fragment easy access to the business logic of the application.

This leads to a bunch of problems:

  • Different  Activities and Fragments require different functionality, resulting in such a Singleton bulking and becoming the so-called “God Object” that stores excessive data or does too much. This breaks the Single Responsibility Principle.
  • Activities, Fragments, and other classes using such a Singleton, get an implicit dependence. It is not present in the constructors of class fields and you need to see the code to find it.
  • As the result of the previous point, the classes become tightly bound to a Singleton which makes them unreusable and difficult to test. Such a project is hard to apply changes to as any alteration can cause post effects in any other place of the project.  

To avoid the impression that it’s only about the usage of Singletons, it’s fair to say that the problem is much deeper than that. Often times class constructors and onCreate methods of Activities and Fragments get populated with other objects. For example, there is  SchedulerActivity which displays a schedule and another class, we’ll call it SchedulerManager implements the schedule logic. The SchedulerManager instance will most likely be created in the Activity’s onCreate method.

This type of code is flawed with one problem: the SchedulerActivity class is closely attached to the SchedulerService class:

  • You won’t be able to write unit tests for SchedulerActivity in detachment from SchedulerManager. In fact, you’ll have an integration test instead of a unit test.
  • If later on, you will require multiple different schedule managers with the option of switching between them automatically, you will have to rewrite SchedulerActivity.
  • If SchedulerManager requires configurations, it will have to be reconfigured in every class that uses it.

The Solution

So how do Dependency Injection and Dagger help solve these and the similar problems?

According to the Single Responsibility principle, all the worries about building the required dependencies are delegated to the external and specifically intended for this mechanism, a.k.a Dagger. Due to this, the objects that need dependencies upon initialization can get them automatically. So, Dagger is no less than an additional layer capable of “cutting” strong bindings between classes, making the code modular.

This is what SchedulerActivity will look like if it utilizes Dagger:

class SchedulerActivity : AppCompatActivity() {

@Inject
lateinit var schedulerManager: SchedulerManager

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_scheduler)

application.daggerComponent.inject(this)
}
} 

With the help of the @Inject annotation, we let Dagger know which dependencies we need, and then request them by means of the application.daggerComponent.inject(this) call.

Here, SchedulerManager may or may not be a Singleton, doesn’t matter. SchedulerActivity has no clue about the details on its creation and initialization. All these concerns are passed on to the Dagger settings. We can easily substitute the implementation of SchedulerManager with a stub for writing unit tests.

Dagger also creates the option of providing as many dependencies as you want. There is no need to have a godlike Singleton that stores all the functionality for  Activities and Fragments to gain access to it. You can easily allocate and group the functionality features according to the needs of different project parts.

Selected sources:

Dagger 2. Часть первая. Основы, создание графа зависимостей, Scopes

Dagger 2. Часть вторая. Custom scopes, Component dependencies, Subcomponents

Dagger 2 official page

Google Dagger 2 presentation

Android guides: Dependency Injection with Dagger 2