查看: 622|回复: 0

[Android教程] 【Kotlin中使用Dagger2】进阶提升篇(三)

发表于 2018-1-30 08:00:01
概述

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

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

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

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

  1. /*
  2. 业务级Component
  3. */
  4. @PerModelScope
  5. @Component(dependencies = [ActivityComponent::class],modules = [(AnimalModule::class)])
  6. interface AnimalComponent {
  7. fun inject(activity:AnimalActivity)
  8. }
复制代码
  1. /*
  2. 业务级Module
  3. */
  4. @Module
  5. class AnimalModule {
  6. @Provides
  7. fun provideCatService(service: CatServiceImpl):AnimalService{
  8. return service
  9. }
  10. @Provides
  11. fun provideDogService(service: DogServiceImpl):AnimalService{
  12. return service
  13. }
  14. }
复制代码
  1. /*
  2. 动物 接口
  3. */
  4. interface AnimalService {
  5. fun eat()
  6. }
复制代码
  1. /*
  2. 动物"猫" 实现类
  3. */
  4. class CatServiceImpl @Inject constructor():AnimalService{
  5. override fun eat() {
  6. d("eat","Cat")
  7. }
  8. }
复制代码
  1. /*
  2. 动物"狗" 实现类
  3. */
  4. class DogServiceImpl @Inject constructor():AnimalService{
  5. override fun eat() {
  6. d("eat","Dog")
  7. }
  8. }
复制代码
  1. /*
  2. Actvity
  3. */
  4. class AnimalActivity : BaseActivity() {
  5. @Inject
  6. lateinit var mCatService:AnimalService
  7. override fun onCreate(savedInstanceState: Bundle?) {
  8. super.onCreate(savedInstanceState)
  9. setContentView(R.layout.activity_animal)
  10. initInjection()
  11. mCatService.eat()
  12. }
  13. /*
  14. Dagger2注入注册
  15. */
  16. private fun initInjection() {
  17. DaggerAnimalComponent.builder().activityComponent(mActivityComponent).animalModule(AnimalModule()).build().inject(this)
  18. }
  19. }
复制代码

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

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

@Named

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

  1. @Qualifier
  2. @Documented
  3. @Retention(RUNTIME)
  4. public @interface Named {
  5. /** The name. */
  6. String value() default "";
  7. }
复制代码

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

  1. /*
  2. 业务级Module
  3. */
  4. @Module
  5. class AnimalModule {
  6. @Named(value ="cat" )
  7. @Provides
  8. fun provideCatService(service: CatServiceImpl):AnimalService{
  9. return service
  10. }
  11. @Named(value = "dog")
  12. @Provides
  13. fun provideDogService(service: DogServiceImpl):AnimalService{
  14. return service
  15. }
  16. }
复制代码

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

  1. @field:[Named ("cat")]
  2. @Inject
  3. lateinit var mCatService:AnimalService
复制代码

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

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

  1. /*
  2. Actvity
  3. */
  4. class AnimalActivity : BaseActivity() {
  5. @field:[Named ("cat")]
  6. @Inject
  7. lateinit var mCatService:AnimalService
  8. @field:[Named ("dog")]
  9. @Inject
  10. lateinit var mDogService:AnimalService
  11. override fun onCreate(savedInstanceState: Bundle?) {
  12. super.onCreate(savedInstanceState)
  13. setContentView(R.layout.activity_animal)
  14. initInjection()
  15. mCatService.eat()
  16. mDogService.eat()
  17. }
  18. /*
  19. Dagger2注入注册
  20. */
  21. private fun initInjection() {
  22. DaggerAnimalComponent.builder().activityComponent(mActivityComponent).animalModule(AnimalModule()).build().inject(this)
  23. }
  24. }
复制代码

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

自定义限定符

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

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

  1. /*
  2. Cat 限定符
  3. */
  4. @Qualifier
  5. @Documented
  6. @Retention(RUNTIME)
  7. annotation class CatQualifier
复制代码
  1. /*
  2. Dog 限定符
  3. */
  4. @Qualifier
  5. @Documented
  6. @Retention(RUNTIME)
  7. annotation class DogQualifier
复制代码

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

  1. /*
  2. 业务级Module
  3. */
  4. @Module
  5. class AnimalModule {
  6. @CatQualifier
  7. @Provides
  8. fun provideCatService(service: CatServiceImpl):AnimalService{
  9. return service
  10. }
  11. @DogQualifier
  12. @Provides
  13. fun provideDogService(service: DogServiceImpl):AnimalService{
  14. return service
  15. }
  16. }
复制代码

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

  1. @field:[CatQualifier]
  2. @Inject
  3. lateinit var mCatService:AnimalService
  4. @field:[DogQualifier]
  5. @Inject
  6. lateinit var mDogService:AnimalService
复制代码

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

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

小结

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

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



回复

使用道具 举报