Dagger 2子组件与组件依赖的区别

151

Dagger 1的plus()方法是我之前经常使用的,因此我了解您可能希望拥有完全访问父图绑定的子组件的情况。

在什么情况下使用组件依赖会比子组件依赖更有益,为什么?

4个回答

258

组件依赖 - 当您想要保持两个组件独立时使用此选项。

子组件 - 当您想要将两个组件耦合在一起时使用此选项。


我将使用以下示例来解释组件依赖子组件。 有关示例的一些值得注意的点是:

  • SomeClassA1可以在没有任何依赖关系的情况下创建。 ModuleA通过provideSomeClassA1()方法提供了SomeClassA1的实例。
  • SomeClassB1无法在没有SomeClassA1的情况下创建。 只有在将SomeClassA1实例作为参数传递给provideSomeClassB1()方法时,ModuleB才能提供SomeClassB1的实例。
@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会负责将SomeClassA1的实例作为参数传递给ModuleB上的provideSomeClassB1()方法,每当声明依赖ModuleB的Component/Subcomponent被初始化时。我们需要指示Dagger如何满足依赖关系。这可以通过使用组件依赖子组件来实现。

组件依赖

请注意以下组件依赖示例中的几点:

  • ComponentB必须通过@Component注解上的dependencies方法定义依赖关系。
  • ComponentA不需要声明ModuleB。这使得这两个组件保持独立。
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)

在子组件的例子中,请注意以下几点:

  • 由于ComponentB没有定义对ModuleA的依赖关系,它无法独立存在。因此,它变成了取决于提供ModuleA的组件。因此,它具有一个@Subcomponent注解。
  • ComponentA通过接口方法componentB()声明了ModuleB。这使得这两个组件紧密耦合。事实上,ComponentB只能通过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);
    }
}

6
我有一个子组件设置,它不会将模块B添加到ComponentA中,这意味着ComponentA构建器不需要moduleB。这似乎按照我的预期工作,允许在应用程序启动时创建ComponentA,然后实例化m。 - FriendlyMikhail
2
@MikeN - 你能否说明一下如何在ComponentA中摆脱ModuleB?只有在为ComponentA和ComponentB提供不同的作用域时,我才能在ComponentA上摆脱ModuleB。 - Praveer Gupta
1
你说得对,我的设置有效,因为它们处于不同的范围。抱歉。 - FriendlyMikhail
2
"SomeClassB1 依赖于 SomeClassA1ComponentA 必须明确定义依赖关系。" ==> 你是不是想说 "ComponentB 必须明确定义依赖关系"? - Tar
1
与@Tar所指的类似,我理解在“SomeClassB1依赖于SomeClassA1ComponentA不需要显式地定义依赖关系。”你想说的是"ComponentB不需要显式地定义依赖关系。" - Sebas LG
显示剩余7条评论

49
根据文档Component Dependency 通过组件依赖仅让您访问公开的提供方法绑定,即您只能访问在父 Component 中声明的类型。 SubComponent 在声明时具有访问其父级中整个绑定图的权限,即您可以访问其所有 Module 中声明的对象。 假设您有一个包含所有与 Android 相关的内容(例如 LocationServiceResourcesSharedPreference 等)的 ApplicationComponent。您还想拥有 DataComponent,用于管理持久性事务以及用于处理 API 的 WebService。在 DataComponent 中唯一缺少的是位于 ApplicationComponent 中的 ApplicationContext。从 DataComponent 获取 Context 的最简单方法是依赖于 ApplicationComponent。您需要确保在 ApplicationComponent 中明确定义了一个 Context,因为您只能访问已声明的内容。在这种情况下,没有手动工作,也就是说,您不需要在父 Component 中指定 Submodules,也不需要将您的子模块显式添加到父模块中,例如:
MySubcomponent mySubcomponent = myComponent.plus(new ChildGraphModule("child!")); // No need!

现在考虑这样一种情况:您想从DataComponent注入WebService,并且从ApplicationComponent注入LocationService到使用上述@Submodule plus功能绑定的Fragment中。这里很酷的一点是,您要绑定到的组件(ApplicationComponent)不需要公开WebServiceLocationService,因为您立即可以访问整个图。


2
如果我理解正确的话,没有名为@Submodule的接口。这是一个打字错误吗? - Islam Salah
我喜欢这个使用真实生活例子来展示差异的方式。然而,这比阅读文档更令人困惑。如果有更少的“类”作为示例,并且有更多的图片来说明确切的观点,那将会很有帮助。 - sudocoder

21

这是一个带有截图的示例代码,以更好地理解组件和子组件:

组件(Component): 图片描述

  1. AppComponent包含两个声明。
  2. AppComponent初始化为App类。
  3. HomeActivityComponent依赖于AppComponent。
  4. 在HomeActivity中,在DaggerHomeActivityComponent初始化时,我将AppComponent对象作为组成部分。

子组件(SubComponent):

图片描述

  1. AppComponent包含一个或多个子组件。
  2. AppComponent初始化为App类。
  3. 子组件不知道其父组件,只通过包含模块来提供自己的依赖项。
  4. 在HomeActivity中,我使用其父组件注入了子组件。

并且,以下是图片示意图: 图片描述

来源: 链接


3
如果子组件包含AppComponent,那么图表的意义不会更清晰吗? - Florian Walther

1

直到现在我才意识到的另一件事是:

  • @Subcomponent 实例只有一个父组件(尽管不同的组件可以实例化相同的 @Subcomponent 并成为该实例的父级)
  • @Component 可以通过 组件依赖 声明零个、一个或多个“父”组件

1
在第二种情况下,说“@Component”可能有父级可能不正确。相反,“@Component”没有父级,但其他组件可能依赖它(简单地通过组件依赖项使用它)。 - demaksee
@demaksee 我不确定,但我认为如果你将组件层次结构映射出来,你会得到一个DAG,而且我认为在图形上下文中,这种关系被称为父子关系是一种标准方式。如果我们谈论的是Dagger的内部工作原理,那么我想这可能不是正确的词语。 - arekolek

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,