Dagger2进阶

2017-01-14 10:46:24来源:http://www.jianshu.com/p/299671d315d5作者:r17171709人点击

第七城市

之前简单介绍了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);
}

来看看结果



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<ModelD> modelD6;
@Inject
Provider<ModelD> modelD7;

使用时候是这样的


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

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



Lazy与Provider
参考文章

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


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


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




第七城市

最新文章

123

最新摄影

微信扫一扫

第七城市微信公众平台