Fred Porciúncula
Fred Porciúncula

Reputation: 8902

Why is using @Module.subcomponents better than installing a subcomponent via a method on the parent component?

From the docs:

Using @Module.subcomponents is better since it allows Dagger to detect if the subcomponent is ever requested. Installing a subcomponent via a method on the parent component is an explicit request for that component, even if that method is never called. Dagger can’t detect that, and thus must generate the subcomponent even if you never use it.

Does anyone understand exactly what does that mean?

Upvotes: 1

Views: 183

Answers (1)

Jeff Bowman
Jeff Bowman

Reputation: 95654

Dagger can't tell whether any of your component methods are ever called: It is a compile-time framework that generates a component implementation, and it implements every method you put on your component interface.

@Component(modules = YourModule.class)
public interface YourComponent {
  ClassA a();
  ClassB b();

  ExplicitSubcomponent createExplicitSubcomponent(Dep1 dep1, Dep2 dep2);
}

@Module(subcomponents = ImplicitSubcomponent.class)
public abstract class YourModule {
  @Binds ClassC classC(DefaultClassC classCImpl);
}

In the above example we have ClassA, ClassB, and ClassC. Let's say that out of all of those, you only actually need ClassA: They don't actually depend on each other, and you don't actually use the subcomponents.

  • Dagger implements the binding for ClassA and ClassA's transitive closure of dependencies, which is good, because you need it! You put it on the component interface so that Dagger can create implementations for you, including all of ClassA's dependencies.
  • Dagger implements the binding for ClassB, which isn't necessary, but Dagger can't tell: It only knows that b() is defined, so somebody might call up asking for a ClassB someday. Dagger has no insight into whether anyone calls b(), so it has to create and refer to factory implementations for ClassB and everything ClassB transitively depends on.
  • Dagger does not implement a binding for ClassC, because even though you've bound it, nobody requests it. It's not available on the interface, and nobody asks for it internally, so Dagger can omit it safely.

This illustrates Dagger's philiosophy of only compiling what is reachable from the component interface itself. This applies to subcomponents, too:

  • Dagger creates an implementation for ExplicitSubcomponent, because like with ClassB, you've bound it on the interface. Someone might ask for it.
  • Dagger does not create an implementation for ImplicitSubcomponent until it is reachable from ClassA or ClassB. As soon as you do, it does. If nobody asks for ImplicitSubcomponent, Dagger doesn't generate code for it.

Of course, if you're trimming your build with Proguard or some other static analyzer, those tools might be able to trim unnecessary classes or methods. However, at that point you're doing work to codegen the subcomponent, you're doing work to compile that into bytecode, and then Proguard is doing work to remove it. It is much more efficient in large projects if you avoid compiling the subcomponent until you know you need it, which Module.subcomponents enables.

Upvotes: 4

Related Questions