×

Android Dagger2 从零单排(二) @Qualifier

96
MrTangFB
2018.05.08 17:51* 字数 1758

  转发请注明出处: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注解。

Android Dagger2
Web note ad 1