转发请注明出处:https://www.jianshu.com/p/b35a658bb1ba
Dagger2作为Android界最具杀伤力的匕首,本系列文章将用最通俗的语言带领你揭开它的真面目。
边缘OB:从零单排带你从低分局打到高分局,从First Blood(第一滴血)到Holy Shit(超越神的杀戮),每盘Rampage(暴走)不在话下!
Android Dagger2 从零单排(一) 基础注解
Android Dagger2 从零单排(二) @Qualifier
Android Dagger2 从零单排(三) @Scope
Android Dagger2 从零单排(四) Dependencies与SubComponent
在上一篇文章里面,我们介绍了Dagger2最基本的使用以及每个注解的作用,我们用上一篇留下的问题作为开篇:多个构造方法想要@Inject、多个@Provides方法返回同一数据类型,这种情况该如何注入?
我们来介绍另外一个注解:@Qualifier
我们称其为限定标识符,只能用于注解上。通俗点说,就是我们使用这个注解,自定义一个标识符注解来给我们所要区分的依赖打上记号,让Dagger2通过记号来获取依赖。
例子一,没错,还是最简单的例子,还是领导视察民情,小秘上回告诉车场调度员要请个司机,隔壁老王又是车场调度员的关系户,如何才能让隔壁老王如愿以偿呢。
我们先来定义一个标识符注解,注解类型使用@interface关键字:
@Qualifier
public @interface Sign {
}
此时使用了@Qualifier创建了一个标识符注解@Sign,注意,不能注解在构造方法中,所以多构造方法与多个@Provides方法返回同一数据类型的处理方式是一样的,然后我们在例子中使用@Sign:
public class Bus {
private String driver;
public Bus() {
}
public Bus(String driver) {
this.driver = driver;
}
}
public class ParkingActivity extends Activity {
@Sign
@Inject
Bus mBus;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_dagger);
DaggerParkingComponent.create().inject(this);//DaggerParkingComponent类需要编译才会生成
((TextView) findViewById(R.id.text)).setText("属性注入成功 = " + mBus.toString());//重写Bus的toString()方法能看到打印出"隔壁老王",注入成功
}
}
@Component(modules = ParkingModule.class)
public interface ParkingComponent {
void inject(ParkingActivity activity);
}
@Module
public class ParkingModule {
@Provides
public Bus provideBus() {
return new Bus();
}
@Sign
@Provides
public Bus provideBusHasDriver() {
return new Bus("隔壁老王");
}
}
在例子中可以看到,我们在要注解的属性mBus与提供依赖的方法provideBusHasDriver上各添加了一个注解@Sign,此时mBus有了标识,Dagger2只会提供对应标识的依赖,然后注入,如果把mBus的@Sign注释取消,Dagger2就会通过provideBus方法注入Bus。
如果Bus的构造方法再加几个,此时又如何区分呢?其实,标识符也可以有自己的成员变量。(实际上是一个成员方法,说成员变量是为了方便理解)
@Qualifier
public @interface Sign {
String value() default "";
}
我们可以看成定义了一个String 类型的value成员变量,其默认值是空字符串,然后我们改变下@Sign的写法:
public class ParkingActivity extends Activity {
@Sign("laoWang")
@Inject
Bus mBus;
...
}
@Module
public class ParkingModule {
@Sign("noDriver")
@Provides
public Bus provideBus() {
return new Bus();
}
@Sign("laoWang")
@Provides
public Bus provideBusHasDriver() {
return new Bus("隔壁老王");
}
}
因为注入的mBus有@Sign(“laoWang”)标识,Dagger2只会选择老王为司机的车注入,哪天不想要老王了,只需要改变@Sign里面的字符串即可马上把老王踢了。
对于这个成员变量,我有话要说:
1.咋一看有没有纠结为什么要写成value?其实你想写啥就写啥,不过,使用value有个好处就是可以不用写属性名直接指定值,例如把value改成driver:
@Qualifier
public @interface Sign {
String driver() default "";
}
@Sign(driver = "laoWang")
2.能不能不设置默认值?能!每次使用的时候请为其指定值,否则报错,设置了默认值之后,使用时注解不指定值时,使用的是默认值。
3.能加参数么?不能!只能以无形参的方法形式来声明。
4.能不能有多个成员变量?能!多个成员变量并非要全部使用:
@Qualifier
public @interface Sign {
String value() default "";
String value2() default "";
}
@Sign("laoWang")//此时只使用了默认的value一个属性
@Sign(value2 = "laoLi")//此时只使用了value2一个属性
@Sign(value = "laoWang", value2 = "laoWang")//此时只使用了两个属性
5.数据类型有没有要求?有!只能是基本数据类型、String、Enum、Class,包含其一维数组类型。
例子二,其他情况的应用。
(1)方法注入的应用:
@Inject
public void injectBus(@Sign("laoWang") Bus bus) {
mBus = bus;
}
此时@Sign不能注解在方法上,必须注解在对应的注入参数中。
(2)@Provides方法依赖关系的应用:
@Sign("laoWang")
@Provides
public Bus provideBusHasDriver(@Sign("lw") String driver) {
return new Bus(driver);
}
@Sign("lw")
@Provides
public String provideDriverW() {
return "隔壁老王";
}
总结:
标识符具有对应性,在需要依赖注入的地方有标识,则在提供依赖的地方也一定要有,否则报错。(不要跟我说,可以在提供的依赖注解标识,但是需要依赖注入的地方不注解标识,你看明明也正确,但对应不上!……我会打你的!)
标识符的值具有唯一性,其实正确来说只能是相对唯一性,例子一中ParkingModule的提供Bus依赖的@Provides方法,@Sign都必须是唯一的,存在相同的情况则编译报错(除非你写了两个相同的@Sign提供依赖,但压根都没用到,这时候编译也不会报错也能运行,Dagger2机制不使用的自然不会监测!……但是我还是会打你的!),而依赖注入的地方则不受限制。另外这个唯一性,只是对同一返回类型。例如在例子二@Provides方法依赖关系的应用中,我们把@Sign(“lw”)同样也写成@Sign(“laoWang”),是完全没问题的。
最后我想说,有一个现成的限定标识符是可以直接使用的:@Named
@Qualifier
@Documented
@Retention(RUNTIME)
public @interface Named {
String value() default "";
}
比起我们的例子,多了两个注解@Documented和@Retention(RUNTIME)。
@Documented官方解释是文档化,如果一个类型的声明是用Documented来注解的,则其注解将成为注解元素的公共API的一部分,其实没有什么卵用,加不加都可以。
@Retention(RetentionPolicy.RUNTIME)这个注解到现在我还没研究出来到底要不要加,加的话给什么属性,我用上面的例子仔细的研究了Dagger2编译过后生成的所有类(这些原理后面会有介绍),发现没有任何关于@Sign的生成类或者注释说明,为此我大胆猜测,@Qualifier标识符注解其实是在编译期间有用,是作为指引依赖之间的关联而存在的,运行时依赖注入与被依赖注入间的关系已经由Dagger2生成的类定义好,所以我觉得这个@Retention添加不添加没啥区别,不添加的情况,其默认是RetentionPolicy.CLASS,如果有误的话,欢迎留言指正。
Demo源码截我 对应daggerTwo包名
Dagger2 GitHub地址
Dagger2 官网地址
所有的测试实例均基于2.15版本。
下一篇,我们来研究作用域:@Scope注解。