依赖注入框架Dagger2详解(五),中级篇
发布日期:2021-07-01 03:06:16 浏览次数:2 分类:技术文章

本文共 5157 字,大约阅读时间需要 17 分钟。

尽管Dagger2看起来很容易,但其实里面各种细节很值得注意,这一篇我们将学习它的一些进阶用法。

Module定义方法的规则

为@Provides方法添加输入参数

Module中@Provides方法可以带输入参数,其参数由Module集合中的其他@Provides方法提供,或者自动调用构造方法

下面是其他@Provides方法提供的例子

@Modulepublic class FruitModule{    //8输入参数自动使用到provideFruit()的返回值Color.RED    @Provides    public Fruit provideFruit(Color color){                 return new Apple(color,Size.BIG);    }    @Provides    pulic Color provideFruit(){        return Color.RED;    }}

如果找不到@Provides方法提供对应参数的对象,自动调用带@Inject参数的构造方法生成相应对象

@Modulepublic class FruitModule{    @Provides    public Fruit provideFruit(FruitInfo info){//自动查找到FruitInfo中带@Inject的无参构造器并生成实例传入参数info        return new Apple(info);    }}public class FruitInfo{    Color mColor;    Size mSize;    @Inject    FruitInfo(){        mColor=Color.RED;        mSize=Size.BIG;    }}
区分返回类型相同的@Provides方法

当有Fruit需要注入时,Dagger2就会在Module中查找返回类型为Fruit的方法,也就是说,Dagger2是按照Provide方法返回类型查找对应的依赖。但是,当Container需要依赖两种不同的Fruit时,你就需要写两个@Provides方法,而且这两个@Provides方法都是返回Fruit类型,靠判别返回值的做法就行不通了。这就需要使用@Named来区分,如下:

//定义Module@Modulepublic class FruitModule{    @Named("typeA")    @Provides    public Fruit provideApple(){  //提供Apple给对应的mFruitA        return new Apple();    }    @Named("typeB")    @Provides    public Fruit provdeBanana(){ //提供Banana给对应的mFruitB        return new Banana()    }}//定义Component@Component(modules={FruitModule.class}) interface FruitComponent{    //Dagger根据接口自动生成FruitComponent    void inject(Container container);   }//定义Containerclass Container{    @Named("typeA") //添加标记@Name("typeA"),只获取对应的@Name("typeA")的元依赖   @Inject    Fruit mFruitA;     @Named("typeB") //添加标记@Name("typeA"),只获取对应的@Name("typeA")的依赖    @Inject    Fruit mFruitB;    ...    public void init(){         DaggerFruitComponent.creaete().inject(this); //使用FruitComponent的实现类注入     }}

这样,只有相同的@Named的@Inject成员变量与@Provides方法才可以被对应起来。

如果觉得@Named只能用字符串区分不满足需求,你也可以自定义类似@Named的注解,使用元注解@Qualifier可以实现这种注解,比如实现一个用int类型区分的@IntNamed

@Qualifier   //必须,表示IntNamed是用来做区分用途@Documented           //规范要求是Documented,当然不写也问题不大,但是建议写,做提示作用@Retention(RetentionPolicy.RUNTIME)  //规范要求是Runtime级别public @interface IntNamed{    int value();}

接下来使用我们定义的@IntNamed来修改上面FruitA,FruitB的例子如下

//定义Module@Moduleclass FruitModule{    @IntName(1)    @Provides    public Fruit provideApple(){  //提供Apple给对应的mFruitA        return new Apple();    }    @IntName(2)    @Provides    public Fruit provdeBanana(){ //提供Banana给对应的mFruitB        return new Banana()    }}//定义Component@Component(modules={FruitModule.class}) interface FruitComponent{    //Dagger根据接口自动生成FruitComponent    void inject(Container container);   }//定义Containerclass Container{    @IntName(1) //添加标记@IntName(1),只获取对应的@IntName(1)的元依赖     @Inject    Fruit mFruitA;     @IntName(2) //添加标记@IntName(2),只获取对应的@IntName(2)的依赖    @Inject    Fruit mFruitB;    ...    public void init(){         DaggerFruitComponent.creaete().inject(this); //使用FruitComponent的实现类注入     }}
添加多个Module

一个Component可以包含多个Module,这样Component获取依赖时候会自动从多个Module中查找获取,Module间不能有重复方法。添加多个Module有两种方法,一种是在Component的注解@Component(modules={××××,×××}) 添加多个modules,如下

@Component(modules={ModuleA.class,ModuleB.class,ModuleC.class}) //添加多个Modulepublic interface FruitComponent{    ...}

另外一种添加多个Module的方法可以被使用Module中的@Module(includes={××××,×××}),如下

@Module(includes={ModuleA.class,ModuleB.class,ModuleC.class})public class FruitModule{    ...}@Component(modules={FruitModule.class}) //添加多个Modulepublic interface FruitComponent{    ...}

这种使用Module的includes的方法一般用于构建更高层的Module时候使用。

Module实例的创建

上面简单例子中,当调用DaggerFruitComponent.create()实际上等价于DaggerFruitComponent.builder().build()。可以看出,DaggerFruitComponent使用了构造者模式。在构建的过程中,默认使用Module无参构造器产生实例。如果需要传入特定的Module实例,可以使用

DaggerFruitComponent.builder().moduleA(new ModuleA()) //指定Module实例.moduleB(new ModuleB()).build()

如果Module只有有参构造器,则必须显式传入Module实例。

Component定义方法的规则

  • 1.对应上面苹果容器的例子,Component的方法输入参数一般只有一个,对应了需要注入的Container。有输入参数返回值类型就是void
  • 2.Component的方法可以没有输入参数,但是就必须有返回值:
    Step1:返回的实例会先从事先定义的Module中查找,如果找不到跳到Step2
    Step2: 使用该类带@Inject的构造器来生成返回的实例,并同时也会递归注入构造器参数以及带@Inject的成员变量。比如
//定义ComponentB@Component(modules={××××××××})//1.假设Module中没有provideApp()方法,但有provideInfo()interface ComponentB{    Apple apple(); //2.实现类自动返回由Apple(info)构建的实现类}public class Apple{    @Inject    Apple(Info info){//被@Inject标记,使用这个构造器生成实例        ...    }    Apple(){   //不会使用这个构造器,没有被@Inject标记    }}

上述代码会生成ComponentB的实现类DaggerComponetB,调用其apple()方法会自动使用Apple(info)构造器生成实例返回。

  • 3.假设ComponentA依赖ComponentB,B必须定义带返回值的方法来提供A缺少的依赖ComponentA依赖ComponentB的代码如下
//定义ComponentB@Component(modules={××××××××})interface ComponentB{    ...}//定义ComponentA@Component(dependencies={ComponentB.class},modules={××××××××})//使用dependenciesinterface ComponentA{    ...}

这样,当使用ComponentA注入Container时,如果找不到对应的依赖,就会到ComponentB中查找。但是,ComponentB必须显式把这些A找不到的依赖提供给A。怎么提供呢,只需要在ComponentB中添加方法即可,如下

@Component(modules={××××××××})interface ComponentB{    // 假设A中module中找不到apple,banana,oranges,但是B的module有,B必须提供带返回值的方法如下    Apple apple();    Banana banana();    Oranges oranges();}

Container中的@Inject的规则

  • 1.@Inject可以标记Container中的成员变量,但是这些成员变量要求是包级可见,也就是说@Inject不可以标记private类型的成员变量。
  • 2.当@Inject标记成员变量时,查找对应依赖按照以下规则
    该成员变量的依赖会从Module的@Provides方法集合中查找,
    如果查找不到,则查找成员变量类型是否有@Inject构造方法,并注入构造方法且递归注入该类型的成员变量

转载地址:https://menxindiaolong.blog.csdn.net/article/details/90412132 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:依赖注入框架Dagger2详解(六),高级篇
下一篇:依赖注入框架Dagger2详解(四),初级篇

发表评论

最新留言

网站不错 人气很旺了 加油
[***.192.178.218]2024年05月01日 15时10分41秒