热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Dagger2从浅到深(七)

Dagger系列:Dagger2从浅到深(一)Dagger2从浅到深(二)Dagger2从浅到深(三)Dagger2从浅到深(四)Dagger2从浅到深

Dagger系列:

  1. Dagger 2从浅到深(一)
  2. Dagger 2从浅到深(二)
  3. Dagger 2从浅到深(三)
  4. Dagger 2从浅到深(四)
  5. Dagger 2从浅到深(五)
  6. Dagger 2从浅到深(六)
  7. Dagger 2从浅到深(七)
  8. Dagger 2应用于Android的完美扩展库-dagger.android


Demo地址:

  1. DaggerLearn
  2. Kotlin-Dagger-2-Retrofit-Android-Architecture-Components

在使用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 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属性添加子组件,这种方式体现更多的是父组件和子组件之间更多的是包含关系,这种方式更多的用于功能的封装,比如数据库操作,有的界面需要,有的不需要,可以将数据库操作封装成子组件。

这里写图片描述


推荐阅读
  • 通过CreateDirectory命令创建相应的Directory之后,可以将目录的访问权限授予其他用户,这样其他用户就能通过外部表访问很多主机上的文件,而不需要登录到数据库服务器 ... [详细]
  • springboot 事务 抛出异常_Spring Boot(四) 异常处理
    一、参数校验错误1.注解校验注解校验的常见形式是,在JavaBean类中添加javax.validation校验注解,在控制器方法参数前添加Valida ... [详细]
  • PIMPL 是 C++ 中的一个编程技巧,意思为指向实现的指针。具体操作是把类的实现细节放到一个单独的类中,并用一个指针进行访问 ... [详细]
  • MyBatis模糊查询和多条件查询一、ISmbmsUserDao层根据姓名模糊查询publicListgetUser();多条件查询publicList ... [详细]
  • 贴图的支持及设置:关于贴图分辨率的支持及设置的用户指南
    http:hi.baidu.comdbfr2011818itemeef1eac8df31a2d69744520b贴图分辨率虚幻引擎3支持的贴图分辨率是从1x1到4096x4096 ... [详细]
  • Spring @Primary和@Qualifier注解原理解析
    这篇文章主要介绍了Spring@Primary和@Qualifier注解原理解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值, ... [详细]
  • 标签PostgreSQL,Linux,perf,性能诊断,stap,systemtap,strace,dtrace,dwarf,profiler,perf_events,probe ... [详细]
  • 发现很多时候展示一堆文字,需要让局部的某些字变粗啊,变大、变颜色、能点击等等要求,今天在这简单总结下方便日后直接复用(用ht ... [详细]
  • 在ROS系统中,参数读写一般通过xml或者yaml格式的文件,其中yaml用得比较多。这是一种可读性高,轻量级的标记语言,简单好用。对于yaml文件,ros中用的较早版本的yaml- ... [详细]
  • 最近想用js做一个简单的计算器,不过网上的例子好像大部分都是直接从左到右挨个计算,就好像1+2*5,就会先计算1+2,再计算3*5,并没有实现运算符的优先级,这里找到了一种方法实现,来总结一下。不过这 ... [详细]
  • rtemsapi用户指南Elixir代表了相对较新的编程语言,面向更广泛的受众。它于2011年发布,此后一直在开发中。他的主要特征是取消功能范式 ... [详细]
  • 编程语言是从哪蹦出来的——大型伦理寻根现场
    Hello,我是Alex007,一个热爱计算机编程和硬件设计的小白,为啥是007呢?因为叫Alex的人太多了,再加上每天007的生活,Alex007就诞生了。聊一聊编程到底是啥,怎 ... [详细]
  • Linux提权之suid篇
    Linux提权之suid篇不知攻,焉知防一个在安服路上摸索的大三生,记录平时学习笔记suid前言:1.只有可以执行的二进制程序文件才 ... [详细]
  • lazarus使用sqlite3遇到特定字符时出现乱码的Bug
    lazarus使用zeosDB控件时发现SQLite一个奇怪的现象,应用程序编译为win64时,如果输入中国2022中国时出现乱码,win32和linux64正常,开始怀疑的sql ... [详细]
  • IDEA实用插件Lombok
    LombokLombok是一个可以通过简单的注解形式来帮助我们简化消除一些必须有但显得很臃肿的Java代码的工具,通过使用对应的注解,可以在编译源码的时候生成对应的方法。通常,我们所定义的对象和b ... [详细]
author-avatar
耗子很傻爱钻洞
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有