Dagger2进阶

之前简单介绍了Dagger2的使用,我们现在来看下其他高级玩法

@Qualifiers

有一种需求,就是一个类可能有多个不同的构造方法。如果像刚才写Provides那样继续用一个方法来实现,之前我们就说这样是不可行的。那我们应该怎么实现呢?这时候Qualifiers就登场了。
首先在Provides注解的方法添加Qualifiers注解,是可以做到创建构造方法的调用方法的,并且它对目标类采用何种方法去初始化也进行了标注

我们来看下系统自带的@Named注解,它通过@Qualifiers注解实现了通过名称区分不同的依赖

@Qualifier
@Documented
@Retention(RUNTIME)
public @interface Named {
    /** The name. */
    String value() default "";
}

来看看具体的实例

@Module
public class TotalModule {
    int a;
    int b;
    public TotalModule(int a, int b) {
        this.a=a;
        this.b=b;
    }
    @Provides
    @Named("ForModel1")
    public ModelD providesModelD1() {
        return new ModelD(a, b);
    }
    @Provides
    @Named("ForModel2")
    public ModelD providesModelD2() {
        return new ModelD(b, a);
    }
}

分别提供了名叫ForModel1与ForModel2的2个不同的构造方法
这时候看看怎样使用

@Inject
@Named("ForModel1")
ModelD modelD;

@Inject
@Named("ForModel2")
ModelD modelD2;

DaggerTotalComponent.builder().totalModule(new TotalModule(2, 3)).build().inject(this);
Log.d("MainActivity", modelD.getA() + " " + modelD.getB());
Log.d("MainActivity", modelD2.getA() + " " + modelD2.getB());

看看运行结果

07-12 23:45:43.159 30351-30351/com.example.clevo.dagger2demo D/MainActivity: 2 3
07-12 23:45:43.159 30351-30351/com.example.clevo.dagger2demo D/MainActivity: 3 2

当然你也可以不选择使用@Named,你可以自己定义能够理解的注解,直接仿照Named就行了,这边就简单的描述下

@Qualifier
@Documented
@Retention(RUNTIME)
public @interface ForModel1 {
    /** The name. */
    String value() default "";
}

@Provides
@ForModel1
public ModelD providesModelD1() {
    return new ModelD(a, b);
}

@Inject
@ForModel1
ModelD modelD;

这样既可

@Scope

Dagger2可以通过Scope来限定注解作用域,即为组件范围内的单例。其实Singleton也就是一个普通的Scope。我们来看看Singleton

@Scope
@Documented
@Retention(RUNTIME)
public @interface Singleton {}

其实没啥特别的地方,我们照着写一个

@Scope
@Documented
@Retention(RUNTIME)
public @interface ScopeDemo {}

我在Module里面修改一下

@ScopeDemo
@Provides
@ForModel1    
public ModelD providesModelD1() {
    return new ModelD(a, b);
}

然后在Component里面修改一下

@ScopeDemo
@Component(modules = TotalModule.class)
public interface TotalComponent {
    void inject(MainActivity activity);
}

来看看结果

Dagger2进阶_第1张图片
Scope结果

看到了吧,ForModel1注解下的对象都是

综上所述,所有的@Scope在同一个组件@Component下只会有一个实例,那些@xxxScope或者@Singleton都是一个名字而已,真正的操作还是要靠开发者自行处理

多层依赖

组件与组件之间也可以存在复用的关系,一种是通过Component注解中添加dependencies依赖,另一种是通过@Subcomponent注解去完成。我们一个个的看

dependencies

其实dependencies有点像父类的意思。
我们先创建一个新的model,然后再通过Module将这个model提供出来

public class ModelC {
    int a;
    int b;
    public ModelC(int a, int b) {
        this.a=a;
        this.b=b;
    }
    public int getA() {
        return a;
    }
    public void setA(int a) {
        this.a = a;
    }
    public int getB() {
        return b;
    }
    public void setB(int b) {
        this.b = b;
    }
}
@Module
public class TotalModule2 {
    int a;
    int b;
    public TotalModule2(int a, int b) {
        this.a=a;
        this.b=b;
    }
    @Provides
    public ModelC providesModelC1() {
        return new ModelC(a, b);
    }
}

这里跟之前差不多,没什么好说的。下面我们就开始写这个父类的Component

@Component(modules = TotalModule2.class)
public interface TotalComponent2 {
    void inject2(DemoApplication application);
    ModelC getModelC();
}

这里跟之前比有个明显的区别,就是将ModelC单独拿出来了。为什么这么做,我来告诉你。之前已经提到过了,如果你Module中的方法有使用其他类型对象作为入参对象的话,是需要使用Provides来提供这个类型对象的,如果这个类型对象是父类Module中提供出来的对象,那么我们就需要在父类Component中去声明来提供了
下面我们看看子Module里面在什么样的情况下才会让父类提供这个对象

@Provides
public ModelD providesModelD3(ModelC modelC) {
    return new ModelD(modelC.getA(), modelC.getB());
}

再来看看Component里面怎么改造的

@ScopeDemo
@ScopeDemo2
@Component(dependencies = TotalComponent2.class, modules = TotalModule.class)
public interface TotalComponent {
    void inject(MainActivity activity);
}

这里友情提醒一下,如果不需要父类的注入功能的话,这里的inject()是可以省略的

@Subcomponent

@Subcomponent的实现过程也是比较简单的,拿刚才的TotalComponent来举例,说我要扩展这个。那么TotalComponent就变成父类了,添加@Subcomponent的Component就变成子类了。所以在最后引用的时候,就得引用子类去注入,才能同时保证所以需要初始化的数据被正确初始化

@Module
public class TotalModule3 {
    @Provides
    public ModelB providesModelB() {
        return new ModelB(111, 1333);
    }
}

ModelB与之前都一样,没什么好说的,这边就是新增另一个Module,下面看下关键的Componenet

@Subcomponent(modules = TotalModule3.class)
public interface TotalComponent3 {
    void inject3(MainActivity activity);
}

这个就表明TotalComponent3将是一个子类,但是它是谁的子类呢?来看看父类部分的改造

@ScopeDemo
@ScopeDemo2
@Component(dependencies = TotalComponent2.class, modules = TotalModule.class)
public interface TotalComponent {
//    void inject(MainActivity activity);
    TotalComponent3 totalComponent3(TotalModule3 totalModule3);
}

这边尤其要注意父类的inject不能用。
这样,使用过程就变成如下形式

TotalComponent component=DaggerTotalComponent.builder().totalComponent2(((DemoApplication) getApplication()).component()).totalModule(new TotalModule(2, 3)).build();
component.totalComponent3(new TotalModule3()).inject3(this);

这样其中的ModelB也可以顺利使用了

Lazy与Provider

Lazy和Provider都是用于包装需要被注入的类型,Lazy用于延迟加载,Provide用于强制重新加载

@Inject
Lazy modelD6;
@Inject
Provider modelD7;

使用时候是这样的

ModelD modelD6_=modelD6.get();
ModelD modelD7_=modelD7.get();

都是通过get去获取对象,但是请注意下,延迟加载每次获取到的对象都是一样的,强制重新加载每次获取到的值可能不一样,这个得看起作用域是不是相同的

Lazy与Provider

参考文章

Dagger2使用,详细解读和从Dagger1迁移的方法介绍
Dagger2使用详解
Android常用开源工具(2)-Dagger2进阶

本文演示示例已经上传到Github

首次接触Dagger2框架,个人觉得相对于其他框架来说,这玩意还是有些难度的,所以错误之处请多多指点,我也会在空闲时间外根据自己的理解对文章多多改进,谢谢支持

你可能感兴趣的:(Dagger2进阶)