Dagger in a multi-module project without dagger.android

Setting up Dagger in a multi-module project is non-trivial. Usual solution I’ve seen is to use dagger.android. I dare to say I know my way around Dagger but the android library never clicked for me, as it seemed too magical. So didn’t bother using it. And it seems it was the right choice as Google seems to abandon it.

Basics

The pragmatic way of doing things in a single module app in my opinion is to have with ’d dependencies. instance lives in Application subclass, and you access it via casting . I also use (Sub)Components per feature, to inject s. But I’m sure having them in a single module and use assisted injection etc., is fine also. This is pretty standard and common setup.

Where this breaks down, is in a multi-module (gradle) setup. Issue is the cyclical reference nature of and the call site inside of the feature module.

Obviously there are many ways to modularize, but in my opinion the most basic and scalable is modularizing by feature. That yields a diamond shaped module graph, with module for shared code, bunch of which depend on , and at the tip module which depends on features, containing only minimal code.

However now in a feature, given the direction of module dependencies, there is no way to access the AppComponent.

So we need to jump some hoops. Luckily Dagger allows the @Component annotated interface to extend some other interface. This is the key.

Create the base AppComponent interface in

(note that it’s a plain interface, no dagger annotation). Now create the in and slap the annotation on it

Instantiate the component like this

And feature looks like this

Note that in the sample I use Conductor’s Controller instead Fragment, and the ViewModel is a simple interface, not necessarily the Jetpack’ one. But it’s the same concept.

This works and is relatively straight forward. However doesn’t allow for breaking apart :base module, which will grow over time and will be your bottleneck in compile times. So we want to break :base apart. Don’t do this prematurely as it complicates things, but you should have a direct line of sight as how to do it, when needed.

Breaking :base apart

Okay, let’s say we have a . It’s a singleton, you only want one instance per app. is used by and .

Let’s also say it becomes too big, you break it’s implementation into several classes, or you want to share this logic with some other project you’re doing i.e. it warrants its own module. So we extract the from and move it into the new module.

The issue is, how to attach to the , which lives in , where has no knowledge of while still keeping it singleton.

First, should expose a public (or use constructor if you’re that kind of a person).

Second, let’s again assume our which provides

Since now, is no longer present in the definition, how should you pass in the dependencies parameter?

Solution

The trick with Dagger is that @Component(dependencies=[…]) allows to take any random interface, not just @Component annotated ones! The interface just has to have getters which match with the dependency types our module needs. I wish somebody told me this earlier

So, let’s declare a feature module local “partial” app component

Now let the extend it and attach the .

Since extends you can access the instance again via getting the application context, casting it to and casting the to .

That’s it. It is quite to a lot to digest, but at least there’s no magic involved.

Thanks for reading!

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store