Android多模块+Hilt依赖注入冲突问题解决方案求助
Android多模块Hilt运行时动态选择依赖解决方案
方案1:接口抽象+Map绑定工厂模式(推荐纯运行时动态选择场景)
核心思路是用接口解耦Common模块与具体实现,借助Hilt的@IntoMap绑定所有实现类,再通过工厂类根据运行时条件动态获取实例,完全避免在Common模块硬编码限定符。
步骤拆解:
Common模块定义抽象接口
把原来直接依赖的A、B类型替换为抽象接口,让Library A/B各自实现:// Common模块 interface CoreFeature { fun executeBusinessLogic() }Library A/B实现接口并绑定到Map
每个Library用自定义限定符标记自身实现,同时通过@IntoMap将实例绑定到String键的Map中(键名保证唯一即可):// Library A模块 @Qualifier @Retention(AnnotationRetention.BINARY) private annotation class LibraryA class LibraryAFeature @Inject constructor() : CoreFeature { override fun executeBusinessLogic() { // Library A的具体业务逻辑 } } @Module @InstallIn(SingletonComponent::class) object LibraryAModule { @LibraryA @Provides @IntoMap @StringKey("feature_a") fun provideLibraryAFeature(): CoreFeature = LibraryAFeature() }Library B实现对称修改即可:
// Library B模块 @Qualifier @Retention(AnnotationRetention.BINARY) private annotation class LibraryB class LibraryBFeature @Inject constructor() : CoreFeature { override fun executeBusinessLogic() { // Library B的具体业务逻辑 } } @Module @InstallIn(SingletonComponent::class) object LibraryBModule { @LibraryB @Provides @IntoMap @StringKey("feature_b") fun provideLibraryBFeature(): CoreFeature = LibraryBFeature() }Common模块实现动态选择工厂
注入Hilt自动生成的Map<String, CoreFeature>,根据运行时条件返回对应实例:// Common模块 class FeatureSelector @Inject constructor( private val featureMap: Map<String, @JvmSuppressWildcards CoreFeature> ) { fun getSelectedFeature(useLibraryA: Boolean): CoreFeature { val key = if (useLibraryA) "feature_a" else "feature_b" return featureMap[key] ?: throw IllegalStateException("No feature implementation found for key: $key") } }Common模块使用动态依赖
注入FeatureSelector,在业务逻辑中根据条件获取实例:// Common模块业务类 class CommonBusinessLogic @Inject constructor( private val featureSelector: FeatureSelector ) { fun doWork(useLibraryA: Boolean) { val feature = featureSelector.getSelectedFeature(useLibraryA) feature.executeBusinessLogic() } }
方案2:Gradle变体控制(适合编译时确定依赖的场景)
如果选择逻辑在编译阶段就能确定(比如不同产品flavor、环境),可以直接通过Gradle变体控制只引入其中一个Library,从根源避免依赖冲突:
配置示例:
在App模块的build.gradle中定义产品flavor,为不同flavor绑定对应Library:
// App模块build.gradle android { flavorDimensions "feature" productFlavors { useLibraryA { dimension "feature" } useLibraryB { dimension "feature" } } } dependencies { useLibraryAImplementation project(':libraryA') useLibraryBImplementation project(':libraryB') implementation project(':common') }
编译时选择对应flavor即可,Common模块无需修改,Hilt编译阶段只会看到一个实现类,不会报冲突。
方案3:动态特性模块+EntryPoint(适合按需加载场景)
如果Library A/B是可选的、需要按需加载的功能,可将它们做成Android动态特性模块,通过Hilt的@EntryPoint在运行时加载模块后获取实例:
关键步骤:
- 将Library A/B配置为动态特性模块(
build.gradle中添加apply plugin: 'com.android.dynamic-feature') - Common模块定义EntryPoint接口:
// Common模块 @EntryPoint @InstallIn(SingletonComponent::class) interface FeatureEntryPoint { fun getCoreFeature(): CoreFeature } - 运行时加载模块后获取实例:
// 加载模块并使用的逻辑 fun loadFeatureAndUse(useLibraryA: Boolean) { val moduleName = if (useLibraryA) "libraryA" else "libraryB" SplitInstallManager.create(this).startInstall( SplitInstallRequest.newBuilder().addModule(moduleName).build() ).addOnSuccessListener { val entryPoint = EntryPointAccessors.fromApplication( applicationContext, FeatureEntryPoint::class.java ) val feature = entryPoint.getCoreFeature() feature.executeBusinessLogic() } }
内容的提问来源于stack exchange,提问作者RaBaKa 78
相关产品推荐
相关产品推荐

