Android 101 : What does Dagger really generate behind the scenes 👀

Saurabh Kumar
3 min readFeb 13, 2022

Why do we need to know what dagger generates, why can't we just use annotations and be happy with it? It looks magical to me and that's why I wanted to understand what really happens in the annotation processing step aka the code generation.

For understanding this I will go through a simple example where I will declare a Dummy class, a DummyComponent, and use that DummyComponent to get the required dependency inside the MainActivity

1. Created a constructor injected Dummy class to be used as a dependency inside the MainActivity. For each such constructor injected class dagger generates a Factory for getting the instances of this class.

@Singleton
class Dummy @Inject constructor() {

fun test() {
println("test")
}
}

Actually, dagger generates factories for all the dependencies declared as @construtor injected classes as well as @provides method inside Dagger Modules which leads to the creation of the object dependency DAG.
But for simplicity of the generated code, we will leave the latter for further articles !

public final class Dummy_Factory implements Factory<Dummy> {
@Override
public Dummy get() {
return newInstance();
}

public static Dummy_Factory create() {
return InstanceHolder.INSTANCE;
}

public static Dummy newInstance() {
return new Dummy();
}

private static final class InstanceHolder {
private static final Dummy_Factory INSTANCE = new Dummy_Factory();
}
}

This is the class that dagger generated Dummy_Factory. which will eventually get used up inside the generated DaggerDummyComponent for providing the Dummy class instance.

2. I wrote a simple interface and annotated it with @Component annotation, and dagger generated an implementation for this interface with all the dependencies resolved by the object graph it has built by parsing all modules and @Inject constructor classes it knows about.

@Component
@Singleton
interface DummyComponent {

fun getDummy(): Dummy
}

Currently, it doesn't know about any modules since we haven’t added any in the declaration i.e @Component (modules = [DummyModule::class])

public final class DaggerDummyComponent implements DummyComponent {

private final DaggerDummyComponent dummyComponent = this;
private Provider<Dummy> dummyProvider;

private DaggerDummyComponent() {
initialize();
}

public static Builder builder() {
return new Builder();
}

// Just a convenient function for creating Component
public static DummyComponent create() {
return new Builder().build();
}
// This would initialize factories for all the dependencies
// declared inside the Component

private void initialize() {
dummyProvider = DoubleCheck.provider(Dummy_Factory.create());
}
// For each dependency declared inside the Component,
// we get one such getter

@Override
public Dummy getDummy() {
return dummyProvider.get();
}
public static final class Builder {
private Builder() {
}

public DummyComponent build() {
return new DaggerDummyComponent();
}
}
}

The generated DaggerDummyComponent has factories for all the dependencies declared i.e Dummy has a dummyProvider which is an encapsulation on top of Dummy_Factory which will generate instances of the Dummy class

3. Finally we can use the generated classes inside our target classes. In this case, I will use the main activity and get the Dummy dependency here.

class MainActivity : AppCompatActivity() {
lateinit var dummy:Dummy

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

dummy = DaggerDummyComponent.create().getDummy()
dummy.test()
}
}

Details to look out for !
1. Why are we doing all this when we can simply write factories and use them to get all the dependencies we want?
We use dagger when it becomes too complex to manage the Object graph and scope of these dependencies. Otherwise, we will be perfectly fine writing our own factories.

2. This doesn't seem magical at all why did not we use the @inject annotation inside the MainActivity and let dagger do that work for us?
Yes indeed we can do that but generally, @inject is used when we have a lot of injected members inside a class and we want dagger to inject all of it at once for us instead of getting it one by one ourselves.

I will write more about MembersInjector<T> in the next article

leave me a clap if you found this article insightful 😄, Happy Reading !

--

--