Hilt 的关键在于,默认情况下,源代码中的模块 = 应用中安装的模块。
选项 1:单独的代码路径
理想情况下,您将为不同的构建提供替代模块,并通过sourceSets 区分使用哪些模块
在发布源集中:
@InstallIn(ApplicationComponent::class)
@Module
object ReleaseModule {
@Provides
fun provideHttpClient(): OkHttpClient { /* Provide some OkHttpClient */ }
}
在调试源集中:
@InstallIn(ApplicationComponent::class)
@Module
object DebugModule {
@Provides
fun provideHttpClient(): OkHttpClient { /* Provide a different OkHttpClient */ }
}
选项 2:覆盖使用@BindsOptionalOf
如果选项 1 不可行,因为您想覆盖源中仍然存在的模块,您可以使用 dagger可选绑定
@InstallIn(ApplicationComponent::class)
@Module
object Module {
@Provides
fun provideHttpClient(
@DebugHttpClient debugOverride: Optional<OkHttpClient>
): OkHttpClient {
return if (debugOverride.isPresent()) {
debugOverride.get()
} else {
...
}
}
}
@Qualifier annotation class DebugHttpClient
@InstallIn(ApplicationComponent::class)
@Module
abstract class DebugHttpClientModule {
@BindsOptionalOf
@DebugHttpClient
abstract fun bindOptionalDebugClient(): OkHttpClient
}
然后仅在调试配置中的文件中:
@InstallIn(ApplicationComponent::class)
@Module
object DebugHttpClientModule {
@Provides
@DebugHttpClient
fun provideHttpClient(): OkHttpClient { ... }
}
选项 3:多重绑定@IntoMap
如果您需要更多的粒度,只是实现+测试/调试覆盖,您可以使用多重绑定和映射,使用键作为选择实现的优先级。
@InstallIn(ApplicationComponent::class)
@Module
object Module {
@Provides
fun provideHttpClient(
availableClients: Map<Int, @JvmSuppressWildcards OkHttpClient>
): OkHttpClient {
// Choose the available client from the options provided.
val bestEntry = availableClients.maxBy { it.key }
return checkNotNull(bestEntry?.value) { "No OkHttpClients were provided" }
}
}
主要应用模块:
@InstallIn(ApplicationComponent::class)
@Module
object MainModule {
@Provides
@IntoMap
@IntKey(0)
fun provideDefaultHttpClient(): OkHttpClient {
...
}
}
调试覆盖:
@InstallIn(ApplicationComponent::class)
@Module
object DebugModule {
@Provides
@IntoMap
@IntKey(1)
fun provideDebugHttpClient(): OkHttpClient {
...
}
}
如果您使用选项 3,我要么将提供的类型设为可空/可选,要么避免使用@Multibinds
,以便在映射中没有绑定任何内容时,事情会在编译时而不是运行时失败