Why Android Apps uses Dagger 2?

Elye
Elye
Sep 20 · 6 min read
Picture by Markus Spiske in Unsplash

Dagger 2 is so popular, why? Many Android Apps uses it. Even Google advocated using Dagger 2. But why? The iOS developers wonder, some Android developers just use blindly. So thought I should just share it here…

Note: this is not a Tutorial on Dagger 2, but more for general understanding why Dagger 2 is used. To learn Dagger 2 the easy way, refers to Dagger 2 for Dummies

The challenge with Activity and Fragment

In Android, we use Activity and Fragment to form our views on screen. It is the UIViewController of iOS.

For UIViewController, we can send dependencies through the constructor.

let uiController = MyViewController(dependency: myDependency)

But in Android, doing that is NO-NO for both Fragment and Activity.

In Activity

It is obvious as we start an Activity using the below. It is not even calling any constructor. No way to pass any parameter in.

startActivity(Intent(context, StartingActivity::class.java))

In Fragment

It is not as clear here. We instantiate the Fragment first before committing it.

val fragment = MyFragment()
fragmentManager.beginTransaction().add(container, fragment).commit()

But if one add a parameter in like MyFragment(myArgument), it is destined to doom on state restoration. The system can’t restore those arguments.

Of course if we like, we could send data through using Intent for Activity and Bundle for Fragment, but these are mainly for serializable data, and not for dependencies.

Perhaps this limitation is a blessing is disguise, as passing dependencies through constructor is not the best things to do for a large deep system.

Because of this, we need some solution to get our the dependencies not through constructor. The Activity and Fragment need to pull its dependencies instead of being pushed for.

Dependencies container, harder than it sound

The quick solution comes to mind, let’s have something store all our dependencies. In Android, we do have the single Application that is accessible to all.

It is interesting iOS folks is also now considering this approach, as stated in the PointFree episode 16. It recommends a Singleton of Environment object, which is a container that stores all its main dependencies.

For a simple App, I think that would be a great approach.

When the Apps grows in dependencies, we can’t really have just a single containers. We need ways to partition them into sub-containers for related dependencies. We need to wire up dependencies upon dependencies manually. e.g.

val interceptor = Container.providesInterceptor(context)
val okHttpClient = Container.providesOkHttpClient(interceptor)
val networkClient = Container.providesNetworkClient(okHttpClient)

Let’s not forget, for some dependencies, it has to be Singleton, while others constructed specifically per the object need. Some we prefer to have them created per needed basis and not always already constructed.

Soon, we realize need to create our own DI (Dependencies) mechanism. And we probably bump into this blog below, looks it is not as straightforward.

If there’s no good solution out there, perhaps we should consider… but why reinvent the wheel if there’s a good one out there?

Manual DI: Code not as neat

Even if we could have an elegant way to making a container for injecting the needing dependencies, how would we access them?

  1. Direct access through the container? e.g. Container.networkClient
  2. Assign to local variable before using it? i.e. manually wiring up things e.g. val networkClient = Container.networkClient

While acceptable, those create unnecessary boilerplates, that Dagger 2 has help solve using Field Injection.

Dagger 2: What’s needed is mostly in place

Dagger 2 provides great DI capabilities as below

Auto wiring of dependencies

Instead of one has to manually wire up all dependencies. Below is one example.

@Singleton
@Provides
fun providesInterceptor(context: Context): UserAgentInterceptor {
val userAgent = "... some string ..."
return UserAgentInterceptor(userAgent)
}
@Singleton
@Provides
fun providesOkhttpClient(
interceptor: UserAgentInterceptor): OkHttpClient {
val builder = OkHttpClient.Builder()
builder.addNetworkInterceptor(userAgentInterceptor)
setupOkhttpDebugInterceptor(builder)
return builder.build()
}
@Singleton
@Provides
fun providesNetworkClient(okHttp: OkHttpClient)
= NetworkClient(okHttp)

Assuming we need the NetworkClient. We just need to define

@Inject
lateinit var networkClient: NetworkClient

Instead of needing to write this.

val interceptor = Container.providesInterceptor(context)
val okHttpClient = Container.providesOkHttpClient(interceptor)
val networkClient = Container.providesNetworkClient(okHttpClient)

Clean grouping and scoping of dependencies

Dagger 2 allow easy partition of related dependancies into different container (i.e. Component, SubComponent, Module). They could be of different life span (i.e. Scoping, Singleton).

The diagram below illustrate that clearer (note, the partition is just an example. One could architect it differently per apps need).

We could group Activities’s dependency within Activity Component. They could be partitioned to module A and B.

Activity could also need some dependencies from the Application, hence we could make it dependent on the Application Component (Application dependencies could be place in different package library module if we want to, enabling modular App development).

If we want, Fragment could have its own dependencies injected as well, on top of what it gets from the Activities. Instead of forming Component, we could make it Subcomponents, that has the flexibility of accessing all its parent’s component dependencies easily.

With this flexibility in place, the developer just need to think how to architect their dependencies, and not worry about writing them up.

Dagger 2: Well tested, trusted and supported

Just Google “Dagger 2”, and you could find tons of documentations, tutorial and supports available on this library. There’s even a course in Udemy on Dagger 2. Google advocated using Dagger 2. It is also developed following the JSR330. So the supports is definitely there.

Dagger 2 is an improved version from Dagger 1. It is no longer using Reflection, which makes its runtime much efficient than its previous generation. The generated code is relatively minimal, compared to it’s predecessors. (Note: nonetheless we should explore some interesting ways to reduced its generated code further as well as per this blog.)

I have to admit, the learning curve of Dagger 2 is relatively steep for new developer. Even for some veteran, I’m surprise to hear from them, they are still struggling to understand and feel Dagger 2 is too magical. But such a “hard” to learn library, if not for its great benefit, efficiency and capability, it would not have survive this long.

A quick check on some popular Apps, as least on this date, I could see Uber and Twitter app state it is using Dagger 2. Slack, Facebook, SoundCloud, Trello and Reddit stated Dagger on the 3rd party library. It could be referring to Dagger 2. I believe many Google developed app is also Dagger 2 charged DI.


Having said so much about why Android Developer should consider using Dagger 2, I’m in no way against considering other uprising Framework e.g. Koin. I’m all for the better winner of toolset.

I’m blogging here just to educate others why Dagger 2 is considered for Android development, and hopes to eliminate fear in some people of using the framework, due to its steep learning curve, and seemingly magical injection happening behind the scene.

My experience with daggers comes in 3 stages

  1. HATE: It was so hard to learn
  2. LOVE+HATE: It is so magical.
  3. LOVE: After understand how it works, and help the architecture work

If someone use Dagger 2 just because it is magical, it is just half good. Magical is not a good reason to use a tool, as it will come and bite us when somethings is not working anymore, or when it is not meeting our special needs.

So I would encourage all to have as much understanding on it before make a final conclusion of using it or leaving it. You could test out your knowledge of Dagger in the link below.


I hope this post is helpful to you. You could check out my other interesting topics here.

Follow me on medium, Twitter, Facebook or Reddit for little tips and learning on Android, Kotlin etc related topics. ~Elye~

Elye

Written by

Elye

Learning and Sharing Android and iOS Development

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade