Dagger 2从浅到深(七)

在使用Dagger 2开发时,一般都是在Application中生成一个AppComponent,然后其他的功能模块的Component依赖于AppComponent,作为AppComponent的子组件。可是,对于将自组建添加到父组件有两种方式:

  1. 通过@Component的dependencies属性依赖父组件

    @Component(modules = OrangeModule.class, dependencies = FruitComponent.class)
    public interface OrangeComponent {
        ***
    }
    
  2. 通过Moudle的subcomponents属性添加子组件
    @Module(subcomponents = AppleSubcomponent.class)
    public class FruitModule {
    
        ***
    }
    

自己也是照葫芦画瓢采用了第一种方式,对于子组件(@Subcomponent)的认识也是一知半解,总是不能合理的封装,本篇文章深入了解@Subcomponent。

Subcomponent的介绍

对于继承,大家应该都不陌生,其实就是子类继承父类,子类自动获取了的父类的属性和行为。我觉得我们也可以这么认为子组件。子组件是继承和扩展父组件的对象的组件。我们可以将应用程序的各个功能分割为模块,以将应用程序的不同部分彼此封装或在组件中使用多个范围。这不就是Android的模块化开发。

  • 绑定在子组件中的对象可以取决于绑定在其父组件或任何继承自组件中的任何对象,以及绑定在其自己的模块中的对象。
  • 绑定在父组件中的对象不能依赖于在子组件中绑定的对象; 绑定在一个子组件中的对象也不能取决于兄弟子组件中绑定的对象。

换句话说,相对于对象而言,自组件这么说 - 你的是我的,我的还是我的。父组件只能这么说 - 你的不是我的,我的是我的。

添加子组件

声明子组件

声明子组件,与普通组件声明类似,可以通过编写一个抽象类或接口来创建一个子组件,该类或接口声明了返回应用程序相关的类型的抽象方法。可以使用@Component,也可以使用@Subcomponent注解,这个没有一定的强制性。

与@Component注解不同的是,使用@Subcomponent注解子组件,必须声明其xxComponent.Builder,否则编译时,会报错。

@Subcomponent(modules = AppleModule.class)
public interface AppleSubcomponent {

    AppleBean supplyApple();

    @Subcomponent.Builder
    interface Builder{
        Builder appleModule(AppleModule module);
        AppleSubcomponent build();
    }
}

通过@Component的dependencies属性依赖父组件

  1. 声明Module:OrangeModuleModule和FruitModule,其中OrangeModuleModule提供一个OrangeBean对象,而FruitModule提供一个Fruit对象

    @Module
    public class OrangeModule {
    
        @Provides
        public OrangeBean provideOrange() {
            return new OrangeBean("这是一个橘子");
        }
    }
    
    @Module
    public class FruitModule {
    
        @Provides
        public Fruits provideFruit() {
            return new Fruits("这是一个水果");
        }
    }
    
  2. 声明Component,并添加依赖关系
    @Component(modules = OrangeModule.class, dependencies = FruitComponent.class)
    public interface OrangeComponent {
        void inject(OrangeFragment fragment);
    }
    
    @Component(modules = {FruitModule.class})
    public interface FruitComponent {
    
        ***
    
        // 将FruitModule中的Fruits暴露出来,以便于其他依赖于FruitComponent的Component调用
        // 若不将Fruits暴露出来,依赖于FruitComponent的Component无法获取该实例,此时编译会报错,提示为该实例提供@Inject注解或者@Provides方法
        Fruits supplyFruits();
    }
    
  3. 注入依赖对象

    public class OrangeFragment extends Fragment {

    ***
    
    @Inject
    OrangeBean mOrangeBean;
    @Inject
    Fruits mFruits;
    
    public OrangeFragment() {
    }
    
    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        FruitComponent fruitComponent = DaggerFruitComponent.create();
        OrangeComponent orangeComponent = DaggerOrangeComponent.builder()
                .fruitComponent(fruitComponent)
                .build();
        orangeComponent.inject(this);
        super.onCreate(savedInstanceState);
    }
    
    ***
    

    }

  4. 可以清晰的看出依赖关系,在示例中,OrangeComponent依赖于FruitComponent
  5. 对于每个Component都会生成DaggerXxComponent实例

注意:

依赖Component(OrangeComponent) 仅继承 被依赖Component(FruitComponent) 中显示提供的依赖。如果被依赖Component(FruitComponent)必须提供依赖注入的对象,也就是将对象实例暴露出来。否则,在子组件,也就是依赖Component(OrangeComponent)中,无法使用@Inject注入被依赖的Component(AppComponent)中的对象,此时编译器会报错,提示依赖注入的Xx实例没有提供@Inject注解或者@Provides方法。

通过Moudle的subcomponents属性添加子组件

  1. 声明AppleModule

    @Module
    public class AppleModule {
    
        @Provides
        public AppleBean privdeApple() {
            return new AppleBean("这是一个苹果");
        }
    }
    
  2. 声明@SubComponent

    @Subcomponent(modules = AppleModule.class)

    public interface AppleSubcomponent {

    //

    AppleBean supplyApple();

    @Subcomponent.Builder
    interface Builder{
        Builder appleModule(AppleModule module);
        AppleSubcomponent build();
    }
    

    }

  3. 声明FruitModule,将AppleSubcomponent添加到subcomponents中
    @Module(subcomponents = {AppleSubcomponent.class})
    public class FruitModule {
    
        @Provides
        public Fruits provideFruit() {
            return  new Fruits("这是一个水果");
        }
    
        @Provides
        @Type(value = "apple")
        public Fruits provideSubApple(AppleSubcomponent.Builder builder) {
            return new Fruits(builder
                    .appleModule(new AppleModule())
                    .build()
                    .supplyApple()
                    .toString());
        }
    }
    
  4. 声明FruitComponent,并将AppleSubcomponent.Builder暴露出来,以便依赖注入子组件的Builder
    @Component(modules = {FruitModule.class})
    public interface FruitComponent {
    
        void inject(FruitActivity activity);
    
        ****
    
        AppleSubcomponent.Builder supplyAppleSubcomponentBuilder();
    }
    
  5. 依赖注入对象
    public class FruitActivity extends AppCompatActivity {
    
        ***
    
        // 依赖注入子组件的Builder
        @Inject
        Provider<AppleSubcomponent.Builder> appleBulder;
        AppleSubcomponent mAppleSubcomponent;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            FruitComponent fruitComponent = DaggerFruitComponent.builder()
                    .build();
            fruitComponent.inject(this);
            super.onCreate(savedInstanceState);
            setContentView(getLayoutId());
    
            ***
    
            mAppleSubcomponent = appleBulder.get().build();
            AppleBean mAppleBean = mAppleSubcomponent.supplyApple();
        }
    
        ***
    }
    

关于SubComponent:

  1. 不需要在被依赖的Component中显示提供依赖
  2. @Subcomponent不会生成DaggerXxComponent实例,也就意味着不需要使用更多的DaggerXXXXComponent对象来创建依赖。
  3. 将子组件添加到父组件中,只需将所注入的@Module的subcomponents属性中,然后父组件可以访问子组件的Builder

@Scope

Dagger 2从浅到深(四)已经提到,在Dagger中,通过使用@Scope对组件进行注解,可以将组件与组件的生命周期相关联,实际上对注入器的控制。在这种情况下,组件实现保存对所有作用域对象的引用,从而可以重用它们。@Scope注解的@Provides方法的Module只能注入到到具有相同作用域的注解的组件中。

对于@Inject注解构造函数的类,也可以使用@Scope进行注解,这些“隐式绑定”可以被该作用域或任何其后代组件注释的任何组件使用,作用域实例将被绑定在被注解的作用域范围内。

正是这样,任何子组件与父组件不能使用相同的@Scope注解,因为子组件是在父组件中创建的,所以其生命周期应在父组件生命周期范围内,意味着子组件的生命周期与父组件的生命周期存在包含关系。比如:

@RootScope @Component
interface RootComponent {
  BadChildComponent.Builder badChildComponent(); // ERROR!
  SiblingComponentOne.Builder siblingComponentOne();
  SiblingComponentTwo.Builder siblingComponentTwo();
}

@RootScope @Subcomponent
interface BadChildComponent {...}

@ChildScope @Subcomponent
interface SiblingComponentOne {...}

@ChildScope @Subcomponent
interface SiblingComponentTwo {...}

在示例中,BadChildComponent具有与父对象RootComponent相同的@RootScope注解,这是一个错误注解。但是SiblingComponentOne和SiblingComponentTwo都使用@ChildScope,因为两个子组件绑定同一个对象并不会产生混淆。

对于子组件而言,两个子组件使用相同的@Scope注解,这两个子组件内容的@Scope注解的实例是相对独立,互不关联。

@Singleton @Component
interface RootComponent {
  SessionComponent.Builder sessionComponent();
}

@SessionScope @Subcomponent
interface SessionComponent {
  FooRequestComponent.Builder fooRequestComponent();
  BarRequestComponent.Builder barRequestComponent();
}

@RequestScope @Subcomponent
interface FooRequestComponent {...}

@RequestScope @Subcomponent
interface BarRequestComponent {...}

在示例中,RootComponent使用@Singleton注解。@SessionScope嵌套在@Singleton范围内,@RequestScope嵌套在@SessionScope中。值得注意的是,FooRequestComponent和BarRequestComponent尽管都用@RequestScope注解,但是它们都是SessionComponent的子组件,是相互独立的。

  • RootComponent使用@Singleton注解
  • @SessionScope嵌套在@Singleton范围内
  • @RequestScope嵌套在@SessionScope中
  • FooRequestComponent和BarRequestComponent都用@RequestScop注解

复用Module

当相同的Module类型注入在父组件及其任何子组件中时,则每个组件将自动使用该模块的相同实例。 这意味着如果您为重复的模块调用子组件构建器方法,或者子组件工厂方法将重复的模块定义为参数,则会出现错误。(前者在编译时无法检查,因此是运行时错误。)

@Component(modules = {RepeatedModule.class, ...})
interface ComponentOne {
  ComponentTwo componentTwo(RepeatedModule repeatedModule); // COMPILE ERROR!
  ComponentThree.Builder componentThreeBuilder();
}

@Subcomponent(modules = {RepeatedModule.class, ...})
interface ComponentTwo { ... }

@Subcomponent(modules = {RepeatedModule.class, ...})
interface ComponentThree {
  @Subcomponent.Builder
  interface Builder {
    Builder repeatedModule(RepeatedModule repeatedModule);
    ComponentThree build();
  }
}

DaggerComponentOne.create().componentThreeBuilder()
    .repeatedModule(new RepeatedModule()) // 运行时报错
    .build();

总结

好像有点懂了,从下图更能清楚地看清楚,两种添加子组件的区别:

  1. 通过@Component的dependencies属性依赖父组件,这种方式体现更多的是父组件和子组件之间更多的是依赖关系。比如,Appcomponent中提供Context,以便其他子Component依赖注入。
  2. 通过Moudle的subcomponents属性添加子组件,这种方式体现更多的是父组件和子组件之间更多的是包含关系,这种方式更多的用于功能的封装,比如数据库操作,有的界面需要,有的不需要,可以将数据库操作封装成子组件。

时间: 2024-12-25 22:32:32

Dagger 2从浅到深(七)的相关文章

Dagger 2从浅到深(六)

之前用到的,都是基于单个实例的依赖注入.强大的Dagger也支持多个元素的依赖注入,将注入的元素聚合到Set或者Map中,以便应用程序代码可以注入Set/Map,而不依赖于单独的绑定. 多个元素绑定并注入到Set 普通Set注入 将多个元素注入到Set中,不仅支持单个元素注入到Set中,同时支持子Set<T>注入到Set中. 将单个元素注入到Set: @Module() public class FruitModule { @Provides @IntoSet public BananaBea

浅入深出ElasticSearch构建高性能搜索架构

浅入深出ElasticSearch构建高性能搜索架构  课程学习地址:http://www.xuetuwuyou.com/course/161 课程出自学途无忧网:http://www.xuetuwuyou.com 一.课程用到的软件 ElasticSearch5.0.0 Spring Tool Suite 3.8.2.RELEASE Maven3.0.5 Spring4 Netty4 Hadoop2.7.1 Kibana5.0 JDK1.8.0_111 二.课程目标 1.快速学习Elastic

由浅到深学习JDBC二

封装数据访问对象 1:通过分析总结,所有对数据库表的操作都可以总结为通过JDBC对表的增删改查,为了减少冗余代码, 使得每次操作表时,不必都写JDBC程序,所以将对一张表的所有数据访功能,封装在数据访问对象 (Data Access Object)中,方便调用. 2:为了方便数据传输,往往会将java程序中所有相关操作的零散字段值,封装成一个实体对象--entity. 实体封装原则: 表----实体类 字段---属性 实现序列化 提供set,get方法. 以下代码就是利用Dao数据访问对象写出的

由浅到深学习JDBC一

JDBC: 虽然由于快节奏的开发,编程速度的追求,越爱越多的MVC框架出现,比如持久层的hibernate, mybatis等等,他们对Dao层的支持都很强大,既快速,又简便.但是他们的底层同样是使用了JDBC, 为了追求高速简便,我们可以不使用JDBC,但一定要了解JDBC.了解JDBC也有助于学习其他持久层框架. java和数据库交互需要中间程序作为中转.在很早以前,数据库厂商还没有一套统一的API作为 java语言和数据库的接口,开发程序是一件很头疼的事.对不同的数据库需要写不同的程序来作

SE壳C#程序-CrackMe-爆破 By:凉游浅笔深画眉 / Net7Cracker

[文章标题]: [SE壳C#程序-CrackMe-爆破]文字视频记录![文章作者]:  凉游浅笔深画眉[软件名称]: CM区好冷清,我来发一个吧!小小草莓[下载地址]: http://www.52pojie.cn/thread-243089-1-1.html[加壳方式]: SE壳[使用工具]: OD+WinHex+CFF Explorer[作者声明]: 只是感兴趣,没有其他目的.失误之处敬请诸位大侠赐教!思路来自吾爱大牛:Pnmker的教程谢谢!-------------------------

浅入深出之Java集合框架(下)

Java中的集合框架(下) 由于Java中的集合框架的内容比较多,在这里分为三个部分介绍Java的集合框架,内容是从浅到深,哈哈这篇其实也还是基础,惊不惊喜意不意外 ̄▽ ̄ 写文真的好累,懒得写了.. 温馨提醒:建议从(上)开始看哦~ 目 录 浅入深出之Java集合框架(上) 浅入深出之Java集合框架(中)   浅入深出之Java集合框架(下) 前 言 在<浅入深出之Java集合框架(中) >中介绍了Map接口的基本操作.使用的示例是在<浅入深出之Java集合框架(上)>中的模拟学

浅入深出之Java集合框架(上)

Java中的集合框架(上) 由于Java中的集合框架的内容比较多,在这里分为三个部分介绍Java的集合框架,内容是从浅到深,如果已经有java基础的小伙伴可以直接跳到<浅入深出之Java集合框架(下)>. 目录: 浅入深出之Java集合框架(上) 浅入深出之Java集合框架(中)   努力赶制中..关注后更新会提醒哦! 浅入深出之Java集合框架(下) 努力赶制中..关注后更新会提醒哦! 一.集合概述 1)集合的概念 现实生活中的集合:很多事物凑在一起. 数学中的集合:具有共同属性的事物的总体

浅入深出之Java集合框架(中)

Java中的集合框架(中) 由于Java中的集合框架的内容比较多,在这里分为三个部分介绍Java的集合框架,内容是从浅到深,如果已经有java基础的小伙伴可以直接跳到<浅入深出之Java集合框架(下)>. 目 录 浅入深出之Java集合框架(上) 浅入深出之Java集合框架(中)   浅入深出之Java集合框架(下) 努力赶制中..关注后更新会提醒哦! 前 言 在<浅入深出之Java集合框架(上)>中介绍了List接口和Set接口的基本操作,在这篇文章中,我将介绍关于Map接口的基

『浅入深出』MySQL 中事务的实现

在关系型数据库中,事务的重要性不言而喻,只要对数据库稍有了解的人都知道事务具有 ACID 四个基本属性,而我们不知道的可能就是数据库是如何实现这四个属性的:在这篇文章中,我们将对事务的实现进行分析,尝试理解数据库是如何实现事务的,当然我们也会在文章中简单对 MySQL 中对 ACID 的实现进行简单的介绍. 事务其实就是并发控制的基本单位:相信我们都知道,事务是一个序列操作,其中的操作要么都执行,要么都不执行,它是一个不可分割的工作单位:数据库事务的 ACID 四大特性是事务的基础,了解了 AC