继续浏览精彩内容
慕课网APP
程序员的梦工厂
打开
继续
感谢您的支持,我会继续努力的
赞赏金额会直接到老师账户
将二维码发送给自己后长按识别
微信支付
支付宝支付

【Kotlin中使用Dagger2】进阶提升篇(三)

雷宇
关注TA
已关注
手记 10
粉丝 1.7万
获赞 251
概述

在前面的文章,我们介绍了依赖注入及作用域的使用,这一小节我们来介绍一下提高篇中最后一小节《限定符》。前面文章如下:
【Kotlin中使用Dagger2】基础入门篇(一)
【Kotlin中使用Dagger2】基础入门篇(二)
【Kotlin中使用Dagger2】进阶提升篇(一)
【Kotlin中使用Dagger2】进阶提升篇(二)

本节内容
  • Qualifier-限定符
  • @Named
  • 自定义限定符 Qualifier-限定符

    经过之前我们的介绍,Dagger2可以入注入普通类、接口及第三方类等,如果在注入接口时,它有多个不同的实现类,Dagger2如何选择使用哪一个实现类来实例化?

首先,我们引入一段完成代码(基于前面的代码封装),看一下这种情况是如何发生的。

    /*
        业务级Component
     */
    @PerModelScope
    @Component(dependencies = [ActivityComponent::class],modules = [(AnimalModule::class)])
    interface AnimalComponent {
        fun inject(activity:AnimalActivity)
    }
    /*
        业务级Module
     */
    @Module
    class AnimalModule {

        @Provides
        fun provideCatService(service: CatServiceImpl):AnimalService{
            return service
        }

        @Provides
        fun provideDogService(service: DogServiceImpl):AnimalService{
            return service
        }
    }
    /*
        动物 接口
     */    
    interface AnimalService {
        fun eat()
    }
    /*
        动物"猫" 实现类
     */
    class CatServiceImpl @Inject constructor():AnimalService{
        override fun eat() {
            d("eat","Cat")
        }

    }
    /*
        动物"狗" 实现类
     */
    class DogServiceImpl @Inject constructor():AnimalService{
        override fun eat() {
            d("eat","Dog")
        }

    }
    /*
        Actvity
     */
    class AnimalActivity : BaseActivity() {

        @Inject
        lateinit var mCatService:AnimalService

        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_animal)

            initInjection()

            mCatService.eat()
        }

        /*
            Dagger2注入注册
         */
        private fun initInjection() {
            DaggerAnimalComponent.builder().activityComponent(mActivityComponent).animalModule(AnimalModule()).build().inject(this)

        }
    }

大家可以看到,接口“ AnimalService”有两个实现类:CatServiceImpl和DogServiceImpl,并且我们在AnimalModule中提供了两个实例化方法,返回结果都是接口AnimalService。
接下来,在AnimalActivity中,我们使用Dagger2注入了一个AnimalService的实例,很遗憾的告诉大家,编译报错。因为Dagger2不知道使用哪个实现类来实例化(CatServiceImpl和DogServiceImpl),我们称这种情况为“注入迷失”。

如何解决这样的问题,Dagger2提供了限定符的概念(本身是Java提供的),使用注解@Qualifier可以声明一个限定符注解(同@Scope,用来声明注解的注解),使用限定符注解就可以标明具体使用哪一个实现类来进行实例化。

@Named

在Scope中,存在默认实现@Singleton;在Qualifier,同样存在具体的实现@Named。我们先看一下它的源码:

    @Qualifier
    @Documented
    @Retention(RUNTIME)
    public @interface Named {

        /** The name. */
        String value() default "";
    }

可以看到,@Named是可以带参数的,它的默认值是""。如何使用它来区别具体的实现类?我们需要修改Moduel,代码如下:

    /*
        业务级Module
     */
    @Module
    class AnimalModule {

        @Named(value ="cat" )
        @Provides
        fun provideCatService(service: CatServiceImpl):AnimalService{
            return service
        }

        @Named(value = "dog")
        @Provides
        fun provideDogService(service: DogServiceImpl):AnimalService{
            return service
        }
    }

我们在工厂方法上使用@Named,给不同的实现添加一个“名称”,一个是"cat”,另一个是“dog"。为了在使用的时候让它根据”名称“来进行不同的实例化。我们还需要在调用层(Activity)声明时加上”名称“区别一下。代码如下:

        @field:[Named ("cat")]
        @Inject
        lateinit var mCatService:AnimalService

和之前相比,多了一行"@field:[Named ("cat")]”,使用"cat"来进行实例化当前这个变量。

请注意: 这是Kotlin中的写法,在Java中直接使用@Named("cat")。具体可参考《Kotlin中使用@Named》
同样,我们可以把另外一个实现类也实例化出来,完整调用代码:

    /*
        Actvity
     */
    class AnimalActivity : BaseActivity() {

        @field:[Named ("cat")]
        @Inject
        lateinit var mCatService:AnimalService

        @field:[Named ("dog")]
        @Inject
        lateinit var mDogService:AnimalService

        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_animal)

            initInjection()

            mCatService.eat()
            mDogService.eat()
        }

        /*
            Dagger2注入注册
         */
        private fun initInjection() {
            DaggerAnimalComponent.builder().activityComponent(mActivityComponent).animalModule(AnimalModule()).build().inject(this)

        }
    }

这样的话,我们就有效的区分了一个接口多个实现类的不同实例化。

自定义限定符

除了使用@Named来进行区别以外,我们也可以自定义限定符来进行区别。自定义的方式和之前自定义Scope类似,使用@Named的源码改一下就行啦。

我们这里就直接自定义两个限定符,一个用来标识”cat",一个用来标识"dog“。代码如下:

    /*
        Cat 限定符
     */
    @Qualifier
    @Documented
    @Retention(RUNTIME)
    annotation class CatQualifier
    /*
        Dog 限定符
     */
    @Qualifier
    @Documented
    @Retention(RUNTIME)
    annotation class DogQualifier

和@Named不同的是,我们没有使用参数,是否需要参数,大家根据实际情况修改即可。
如何使用自定义限定符,同@Named类似,首先需要修改Module:

    /*
        业务级Module
     */
    @Module
    class AnimalModule {

        @CatQualifier
        @Provides
        fun provideCatService(service: CatServiceImpl):AnimalService{
            return service
        }

        @DogQualifier
        @Provides
        fun provideDogService(service: DogServiceImpl):AnimalService{
            return service
        }
    }

接下来需要修改调用层声明:

    @field:[CatQualifier]
    @Inject
    lateinit var mCatService:AnimalService

    @field:[DogQualifier]
    @Inject
    lateinit var mDogService:AnimalService

是不是和@Named很像,只是换成了自定义的限定符,并且没有参数。

请注意,在Java中,是直接使用@CatQualifier和@DogQualifier进行标注。

小结

这一小节我们介绍了限定符的使用及如何自定义限定符,用来解决“注入迷失”的情况。
大家在开发过程中,可以根据实际情况,使用@Named或者自定义限定符。

更多精彩应用《Kotlin打造完整电商APP》

打开App,阅读手记
12人推荐
发表评论
随时随地看视频慕课网APP

热门评论

好,是真的好,买雷宇老师的课真的是物超所值

查看全部评论