Bare Metal Dagger: Wiring Subcomponents
Tags: dagger, android
Published: 2020-07-22 18:34:09

---
title: Bare Metal Dagger: Wiring Subcomponents
published: true
description: two approaches for wiring subcomponents in Dagger
tags: dagger, android
//cover_image: https://direct_url_to_image.jpg
---

This is not a tutorial on Hilt. 

It’s also not a tutorial on Dagger subcomponents. Instead, I want to present a surprising (to me) thing I learned recently about them. I guess you can think of this as documentation for a future me, inevitably confused by subcomponents, and searching for an answer. I hope that this is also useful to others.

I joined my present team about eight months ago. And about six months ago, I had already become so infuriated at the way we were doing dependency injection (DI) that I took a weekend to rewrite all our Dagger-related code from scratch. In doing so, I made a small error which I only began to understand six months later (i.e., last week).

No, I haven’t been fired yet.

My approach to DI follows closely from my approach to software organization in general; that is, many small modules, isolated from each other and independently evolvable (while also following an API contract), and also _relatively_ easily testable.<sup>1</sup> This has led to a DI architecture that looks like (simplified)

![Simplified entity diagram](https://dev-to-uploads.s3.amazonaws.com/i/wh97yywoiz9kdol5lwl7.jpg)

A parent, “application” or singleton-scoped component, followed by _many_ activity-scoped subcomponents, one per `Activity`. Fragments are also injected from their parent activities.

In code, this looked like:

```java
@Singleton
@Component(modules = {
  ApplicationSubcomponentsModule.class
})
public interface ApplicationComponent {
  MainActivitySubcomponent.Factory mainActivityFactory();
}
```

Dagger experts will already see the issue, but don’t worry, we’ll get there.

The activity subcomponent looks like

```java
@ActivityScope
@Subcomponent
public interface MainActivitySubcomponent {

  void inject(MainActivity a);
  void inject(MainFragment f);

  @Subcomponent.Factory interface Factory {
    MainActivitySubcomponent newSubcomponent();
  }
}
```

and finally, the activity injects with

```java
public final class MainActivity extends Activity {
  @Override public void onCreate(Bundle savedInstanceState) { 
    getApplicationComponent()
      .mainActivityFactory()
      .newSubcomponent()
      .inject(this);
    super.onCreate(savedInstanceState);   
  }
}
```
where `getApplicationComponent()` is left as an exercise, and with the understanding that the whole statement can be vastly simplified with some helper classes.

# There are two ways of linking subcomponents to parent (sub)components

Before we go on, I need to add one more piece to the puzzle, which was left out deliberately a moment ago. What is `ApplicationSubcomponentsModule`?

```java
@Module(subcomponents = {
  MainActivitySubcomponent.class
})
public interface ActivitySubcomponentsModule {
}
```

It is a Dagger `@Module` which points to a list of `@Subcomponent`s which are _installed_ on the parent (sub)component. I will refer to this method of installing a subcomponent as the “`Module.subcomponents()` approach.” This is the _first_ way of linking a subcomponent with a parent. What’s the second? Well, that’s this line

```java
MainActivitySubcomponent.Factory mainActivityFactory();
```

which specifically enables

```java
getApplicationComponent()
  .mainActivityFactory()
  .newSubcomponent()
  .inject(this);
```

I will refer to this as the “factory method approach.”

I can now finally tell you the surprising thing I learned: you _don’t_ need both approaches to associate a subcomponent with a parent. I learned this shocking fact when I noticed that I had failed to add a new activity’s subcomponent module into the list at `ActivitySubcomponentsModule`, _and my code still worked_. The shock compounded when I saw this wasn’t the first case of someone forgetting to do this (and I, uh, actually did code review on that first one).

This confusion was the result of a misreading of the Dagger documentation (or maybe bad documentation, but I’m comfortable blaming myself for this). If you look at the javadoc on the `@Component` annotation, you’ll see this:

> Subcomponents are declared by listing the class in the `Module.subcomponents()` attribute of one of the parent component's modules. This binds the `Subcomponent.Builder` or `Subcomponent.Factory` for that subcomponent within the parent component.
>
> Subcomponents **may also** be declared via a factory method on a parent component or subcomponent.

The emphasis there is mine. I missed that the first time, and thought _both_ approaches were required to install a component. Turns out, you can do one or the other. There is a difference, however.

# A tale of two approaches

With the factory method approach, as we’ve already seen, you can write

```java
getApplicationComponent()
  .mainActivityFactory()
  .newSubcomponent()
  .inject(this);
```

and this is often very convenient. It also has the non-trivial benefit of being much easier to maintain and understand than the `Module.subcomponents()` approach (a module which lists subcomponents that are “installed” on a parent).

What is enabled by `Module.subcomponents()`? Well, it’s actually entirely redundant if you’re also using the factory method approach. But if you’re _only_ using this, here’s what you have available to you:

```java
public class Thing {
  private final SomeSubcomponent subcomponent;

  @Inject
  public Thing(Provider<SomeSubcomponent.Factory> factoryProvider) {
    subcomponent = factoryProvider.get().newSubcomponent();
  }
}
```

which I guess is neat.

# tl;dr

Use the factory method approach. It’s easier to write, maintain, understand, and use. It will also let you inject factory providers if you want to ¯\\\_(ツ)\_/¯

# Endnotes

<sup>1</sup> As easy as it ever is to write and run instrumented tests on Android.

<!--
https://mermaid-js.github.io/mermaid-live-editor/#/edit/eyJjb2RlIjoiZXJEaWFncmFtXG4gICAgQXBwbGljYXRpb25Db21wb25lbnQgfHwtLXx7IFhBY3Rpdml0eVN1YmNvbXBvbmVudCA6IHByb3ZpZGVzXG4gICAgQXBwbGljYXRpb25Db21wb25lbnQgfHwtLW97IFhNb2R1bGUgOiB1c2VzXG4gICAgWEFjdGl2aXR5U3ViY29tcG9uZW50IH18LS1veyBZTW9kdWxlIDogdXNlc1xuICAgICIsIm1lcm1haWQiOnsidGhlbWUiOiJkZWZhdWx0In0sInVwZGF0ZUVkaXRvciI6ZmFsc2V9
-->