美文网首页Android-Rxjava&retrofit&daggerAndroid-Dagger2
Android Dagger2 从零单排(二) @Qualifi

Android Dagger2 从零单排(二) @Qualifi

作者: MrTangFB | 来源:发表于2018-05-08 17:51 被阅读422次

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

    相关文章

      网友评论

      • 老西子:大佬有点暴力倾向,一句话不对就要打人:scream:
        MrTangFB:@老西子 不敢当,互相学习,互相提升。
      • debug的浩浩牛:大哥 一共打算写几篇?
        MrTangFB:@xiaohaoWits :smile: 最近忙着研究Python,有点掉节奏了,马上会接着完善。
        debug的浩浩牛:@MrTangFB 可以 加油
        MrTangFB:@xiaohaoWits 还有3篇左右:smile:
      • ace563e5d714:第一次看简书评论,写的真的很棒!最近看dagger看的头晕,这几篇文章浅显易懂,感谢博主的奉献:+1:
        MrTangFB:@河道小螃蟹 谢谢,今天准备发布第四篇,欢迎继续关注,共勉。:smile:

      本文标题:Android Dagger2 从零单排(二) @Qualifi

      本文链接:https://www.haomeiwen.com/subject/nromrftx.html