从一个问题开始
假如我们需要向项目中提供两种Gson实例:一个普通实例,一个带有驼峰-下划线命名转换的实例。
@Module
class GsonModule {
@Singleton
@Provides
fun gsonNormal(): Gson {
return GsonBuilder().create()
}
@Singleton
@Provides
fun gsonNaming(): Gson {
return GsonBuilder()
.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
.create()
}
}
这种情况下我们应该如何获取到想要的依赖项实例呢?
@Singleton
@Component(modules = [GsonModule::class])
interface CommonComponent {
fun getGson(): Gson // Dagger如何知道我们希望获取到
fun getGsonNaming(): Gson // GsonModule中的哪个实例呢?
}
class Class {
// Dagger如何知道我们希望获取到GsonModule中的哪个实例呢?
@Inject
lateinit var gson: Gson
@Inject
lateinit var gsonNaming: Gson
}
如果直接像上面这样写,我们在编译时就会收到一个错误:
错误: [Dagger/DuplicateBindings] com.google.Gson is bound multiple times:
因为对于Gson类型的依赖项,存在两个Provides方法,Dagger无法确定使用哪一个,这种情况被称之为依赖迷失。那么该如何解决这个问题呢?Dagger提供了两种方式来解决此类问题。
Named
@Named注解会为依赖提供方法设置一个标记名称,Dagger在编译时会读取该标记名称,当注入字段或者依赖获取接口也使用同一标记名称时,Dagger就会将该依赖提供方法和对应的依赖注入位置相绑定。
使用方式如下:
-
先使用
@Named标记Module中生成依赖实例的Provides方法。⚠️ `@Named`注解和后面自定义的限定符只能用来修饰`Provides`方法。@Module class GsonModule { @Singleton @Provides @Named("gson-normal") fun gsonNormal(): Gson { ... } @Singleton @Provides @Named("gson-naming") fun gsonNaming(): Gson { ... } } -
使用
@Named标记依赖获取接口或依赖注入位置,注意@Named中定义的标记名称需要和修饰Provides方法的名称一样。@Singleton @Component(modules = [GsonModule::class]) interface CommonComponent { @Named("gson-normal") fun getGson(): Gson @Named("gson-naming") fun getGsonNaming(): Gson } class Class { @Named("gson-normal") @Inject lateinit var gson: Gson @Named("gson-naming") @Inject lateinit var gsonNaming: Gson }
Qualifier
除了使用Dagger预置的@Named注解外,我们还可以使用@Qualifier元注解定义自己的限定符:
@Qualifier
@MustBeDocumented
@Retention(AnnotationRetention.RUNTIME)
annotation class GsonType(val value: String)
// 或者
@Qualifier
@MustBeDocumented
@Retention(AnnotationRetention.RUNTIME)
annotation class GsonTypeNaming {
}
@Qualifier
@MustBeDocumented
@Retention(AnnotationRetention.RUNTIME)
annotation class GsonTypeNormal {
}
自定义限定符的使用方式和@Named一致,只要保证同一类型依赖项的不同Provides方法被不同的限定符修饰即可(限定符的字段取值不同,限定符也不同)。另外自定义限定符的字段可以随意定义,只要保证使用时赋值给字段的参数不一样即可。
@GsonTypeNormal
@Provides fun gsonNormal(): Gson { ... }
@GsonTypeNaming
@Provides fun gsonNaming(): Gson { ... }
@GsonType("Normal")
@Provides fun gsonNormal(): Gson { ... }
@GsonType("Naming")
@Provides fun gsonNaming(): Gson { ... }