前言
偶然间看到了这个关于Dagger小技巧的系列,很实用,也不复杂,在此我搬运转述一下。本文并非翻译,只是概述,想要更详细地了解,请查看原文:
Dagger Party Tricks: Private Dependencies
其它技巧:
Dagger小技巧之OkHttp延迟初始化
Dagger小技巧之Kotlin扩展函数
目的
利用Dagger的Qualifier来隐藏内部依赖。
问题
Dagger module:
@Module
object NetworkModule {
@Provides fun client(): OkHttpClient {
//...
}
@Provides fun retrofit(client: Lazy<OkHttpClient>): Retrofit {
//...
}
}
一般而言,以上模块的是为了提供Retrofit,OkHttpClient仅是Retrofit的内部实现细节,不应该在外部访问OkHttpClient,然而,事实并非如此:
@Module(includes = [NetworkModule::class])
object FeatureModule {
@Provides
fun networkAccessor(client: OkHttpClient): NetworkAccessor {
// ಠ_ಠ
}
}
我们依然可以在外部模块中访问OkHttpClient,这种访问可能是无意的,是对NetworkModule的错误使用,但是却是合法的,这实际上破坏了Dagger Module的封装,是应该避免的。
解决方案
Dagger有一种特性叫限定符(@Qualifier
),默认情况下,Dagger以类型区分依赖注入的对象,如果一个类有两个或者更多的对象,那是会报错的:
@Module
object NetworkModule {
@Provides fun provideName1(): String = “Lucy”
//报错,同一类型String两个对象,无法区分
@Provides fun provideName2(): String = “Lily”
}
这时候就需要用到@Qualifier
,@Qualifier
实际上给Dagger提供了类之外额外的metadata(元数据),这样就可以区分相同类的两个对象了。实际上更准确的说法应该是,@Qualifier
是类型签名(type signature)的一部分,提供@Qualifier
实际上就是提供了一个新的类型,所以String
和@MyCustomQualifier String
在Dagger看来是两种完全不同的类型。
扯了这么多@Qualifier
,这和我们的问题有什么关系呢?当然有关,虽然Dagger Module是公有的,但是我们可以通过私有的Qualifier,来限制Module外的访问:
@Retention(BINARY)
@Qualifier
private annotation class InternalApi
@Module
object NetworkModule {
@Provides
@InternalApi
fun provideClient(): OkHttpClient {
//...
}
@Provides
fun provideRetrofit(
@InternalApi client: Lazy<OkHttpClient>
): Retrofit {
//...
}
}
Java 可以使用内部类的方式:
@Module
public class NetworkModule {
@Retention(CLASS)
@Qualifier
private @interface InternalApi {}
@Provides
@InternalApi
static OkHttpClient provideClient() {
//...
}
@Provides
static Retrofit provideRetrofit(
@InternalApi Lazy<OkHttpClient> client
) {
//...
}
}
由于InternalApi
是私有的,所以InternalApi
限定的类也会成为私有的,在该Module之外都是无法访问的,这样就可以做到良好的封装。
私有限定符是避免内部依赖泄露的好方法。
网友评论