150

Dagger 1's plus() method is something I used quite often in previous applications, so I understand situations where you might want to have a subcomponent with full access to the parent graphs bindings.

In what situation would it be beneficial to use a component dependency instead of a subcomponent dependency and why?

David Medenjak
  • 33,993
  • 14
  • 106
  • 134
Bradley Campbell
  • 9,298
  • 6
  • 37
  • 47

4 Answers4

256

Component dependencies - Use this when you want to keep two components independent.

Subcomponents - Use this when you want to keep two components coupled.


I will use the below example to explain Component dependencies and Subcomponents. Some points worth noticing about the example are:

  • SomeClassA1 can be created without any dependency. ModuleA provides and instance of SomeClassA1 via the provideSomeClassA1() method.
  • SomeClassB1 cannot be created without SomeClassA1. ModuleB can provide an instance of SomeClassB1 only if an instance of SomeClassA1 is passed as an argument to provideSomeClassB1() method.
@Module
public class ModuleA {
    @Provides
    public SomeClassA1 provideSomeClassA1() {
        return new SomeClassA1();
    }
}

@Module
public class ModuleB {
    @Provides
    public SomeClassB1 provideSomeClassB1(SomeClassA1 someClassA1) {
        return new SomeClassB1(someClassA1);
    }
}

public class SomeClassA1 {
    public SomeClassA1() {}
}

public class SomeClassB1 {
    private SomeClassA1 someClassA1;

    public SomeClassB1(SomeClassA1 someClassA1) {
        this.someClassA1 = someClassA1;
    }
}

Dagger will take care of passing the instance of SomeClassA1 as an argument to provideSomeClassB1() method on ModuleB whenever the Component/Subcomponent declaring ModuleB is initialized. We need to instruct Dagger how to fulfill the dependency. This can be done either by using Component dependency or Subcomponent.

Component dependency

Note the following points in the Component dependency example below:

  • ComponentB has to define the dependency via the dependencies method on @Component annotation.
  • ComponentA doesn't need to declare ModuleB. This keeps the two components independent.
public class ComponentDependency {
    @Component(modules = ModuleA.class)
    public interface ComponentA {
        SomeClassA1 someClassA1();
    }

    @Component(modules = ModuleB.class, dependencies = ComponentA.class)
    public interface ComponentB {
        SomeClassB1 someClassB1();
    }

    public static void main(String[] args) {
        ModuleA moduleA = new ModuleA();
        ComponentA componentA = DaggerComponentDependency_ComponentA.builder()
                .moduleA(moduleA)
                .build();

        ModuleB moduleB = new ModuleB();
        ComponentB componentB = DaggerComponentDependency_ComponentB.builder()
                .moduleB(moduleB)
                .componentA(componentA)
                .build();
    }
}

SubComponent

Note the following points in the SubComponent example:

  • As ComponentB has not defined the dependency on ModuleA, it cannot live independently. It becomes dependent on the component that will provide the ModuleA. Hence it has a @Subcomponent annotation.
  • ComponentA has declared ModuleB via the interface method componentB(). This makes the two components coupled. In fact, ComponentB can only be initialized via ComponentA.
public class SubComponent {
    @Component(modules = ModuleA.class)
    public interface ComponentA {
        ComponentB componentB(ModuleB moduleB);
    }

    @Subcomponent(modules = ModuleB.class)
    public interface ComponentB {
        SomeClassB1 someClassB1();
    }

    public static void main(String[] args) {
        ModuleA moduleA = new ModuleA();
        ComponentA componentA = DaggerSubComponent_ComponentA.builder()
                .moduleA(moduleA)
                .build();

        ModuleB moduleB = new ModuleB();
        ComponentB componentB = componentA.componentB(moduleB);
    }
}
Julian A.
  • 10,928
  • 16
  • 67
  • 107
Praveer Gupta
  • 3,940
  • 2
  • 19
  • 21
  • 6
    I have a subcomponent setup that does not add Module B to ComponentA , which then means that the componentA builder does not need moduleB. This seems to work the way I expected allowing creation of ComponentA on application start and then instantiating m – FriendlyMikhail Jun 22 '15 at 11:08
  • I dont believe you need ModuleB declared in annotation of ComponentA. This way you can instantiate ComponentA on application start and then create scoped ComponentBs for example per request or per activity. Seems to work very similar to how "plusing" worked in Dagger1 – FriendlyMikhail Jun 22 '15 at 11:11
  • 2
    @MikeN - Can you highlight how you could get rid of ModuleB on ComponentA? I can get rid of ModuleB on ComponentA only if I provide different scopes on ComponentA and ComponentB. – Praveer Gupta Oct 18 '15 at 03:57
  • 1
    You're right my setup works because they are on different scopes. apologies. – FriendlyMikhail Oct 19 '15 at 14:09
  • 2
    "`SomeClassB1` is dependent on `SomeClassA1`. `ComponentA` has to explicitly define the dependency." ==> have you meant "`ComponentB` has to explicitly define the dependency" ? – Tar Jun 21 '16 at 10:27
  • 1
    Similarly to what @Tar pointed, I understand that in "`SomeClassB1` is dependent on `SomeClassA1`. `ComponentA` need not explicitly define the dependency." you meant "`ComponentB` need not explicitly define the dependency." – Sebas LG Sep 17 '16 at 11:28
  • 1
    @Tar @sebas-lg - you are right. I meant `ComponentB`. I have updated the answer with your suggestions. Thanks for pointing it out. – Praveer Gupta Sep 18 '16 at 16:16
  • 1
    Also "`ComponentA` has to declare `ModuleB`" seems to be some kind of mistake. I don't see anything like it in code. – arekolek Apr 05 '18 at 07:39
  • @arekolek - Please see `@Component(modules = ModuleB.class, dependencies = ComponentA.class)` under class `ComponentDependency` – Praveer Gupta Apr 12 '18 at 02:00
  • 1
    @PraveerGupta: I think you mentioned "Component Dependency" example, but it should be "SubComponent" example. – Akash Patra Jul 06 '18 at 06:53
  • @akashPatra - If you are talking in reference to my reply to @arekolek, you are right. @arekolek - I have made edits to make it more clear that `ComponentA` declared dependency on `ModuleB` via its interface method `componentB()`. – Praveer Gupta Nov 23 '18 at 18:56
  • what happens if I have first implementation but I have scopes too, and I want to pass to the second approach? – coroutineDispatcher May 16 '19 at 14:28
49

According to the documentation:

Component Dependency gives you access to only the bindings exposed as provision methods through component dependencies, i.e. you have access to only types which are declared in parent Component.

SubComponent gives you an access to the entire binding graph from its parent when it is declared, i.e. you have an access to all objects declared in its Modules.

Let's say, you have an ApplicationComponent containing all Android related stuff (LocationService, Resources, SharedPreference, etc). You also want to have your DataComponent where you manage things for persistence along with WebService to deal with APIs. The only thing you lack in DataComponent is Application Context which resides in ApplicationComponent. The simplest way to get a Context from DataComponent would be a dependency on ApplicationComponent. You need to be sure you have a Context explicitly declared in ApplicationComponent because you only have access to declared stuff. In this case, there is no manual work, meaning you don't need to specify Submodules in parent Component and explicitly add your submodule to a parent module like:

MySubcomponent mySubcomponent = myComponent.plus(new ChildGraphModule("child!")); // No need!

Now consider that case where you want to inject WebService from DataComponent and LocationService from ApplicationComponent into your Fragment which binds using the @Submodule plus feature above. The cool thing here is that the component you're binding to (ApplicationComponent) does not need to expose WebService nor LocationService because you have access to the entire graph right away.

Muhammad Umair Shafique
  • 2,475
  • 1
  • 30
  • 39
Eugene
  • 59,186
  • 91
  • 226
  • 333
  • 2
    If I understand correctly, there is no interface called `@Submodule`. Is it a typo? – Islam Salah Mar 14 '19 at 11:45
  • I like how this uses a real life example to showcase the difference. However, this is more confusing than reading the docs. It would help to have less `classes` as examples and more pictures to illustrate the exact point. – sudocoder Apr 14 '20 at 22:02
20

Here is the code example with screenshot for more understanding of Component and SubComponent:

Component: enter image description here

  1. AppComponent contains two declarations.
  2. AppComponent initializes into App class.
  3. HomeActivityComponent is dependent upon AppComponent.
  4. In HomeActivity on initialization of DaggerHomeActivityComponent, I am giving AppComponent object as a composition.

SubComponent:

enter image description here

  1. AppComponent contains SubComponent or SubComponents.
  2. AppComponent initializes into App class.
  3. SubComponent doesn’t know about his ParentComponent. That only providing its own dependencies by including Module.
  4. In HomeActivity I am injecting SubComponent by using its Parent Component.

And the Pictorial Diagram: enter image description here

Source: link

0xAliHn
  • 18,390
  • 23
  • 91
  • 111
1

One other thing that I didn't quite realize until now is that:

  • A @Subcomponent instance has exactly one parent component (although different components can instantiate that same @Subcomponent and be that instance's parent)
  • A @Component may have zero, one, or many "parent" components declared through component dependencies
arekolek
  • 9,128
  • 3
  • 58
  • 79
  • 1
    Probably in second case it isn't correct to say '@Component' may have... parent(s). Rather '@Component' has no parents, but other may be dependent on that (simply use it) via component dependencies. – demaksee Jul 01 '18 at 20:19
  • @demaksee I don't know, it seems to me that if you map out your component hierarchy, you'll get at DAG, and I think it's a standard way to refer to this relation as parent-child in graph context. If we're talking about the inner workings of Dagger, then I guess it might not be the right word. – arekolek Jul 01 '18 at 21:52