如何用Dagger2替代@Singleton创建多份功能集实例
解决Dagger2中同一依赖多实例的作用域问题
核心思路
要实现同一依赖(ProfitStore)的多独立实例,不能再用绑定到Application级别的@Singleton,而是需要为每个功能实例创建独立的Dagger子组件,并通过自定义作用域限定子组件内的依赖生命周期,确保每个子组件拥有自己的ProfitStore实例。
具体实现步骤
1. 定义自定义功能作用域
创建专属的作用域注解,标记属于单个功能实例的依赖,替代@Singleton:
@Scope @Retention(AnnotationRetention.RUNTIME) annotation class ProfitFeatureScope
这个作用域会确保同一子组件内的依赖是单例,但不同子组件实例会生成全新的依赖对象。
2. 创建功能子组件
定义子组件,专门负责提供该功能所需的依赖(包括ProfitStore),并声明注入目标(Fragment、ViewModel):
@ProfitFeatureScope @Subcomponent(modules = [ProfitFeatureModule::class]) interface ProfitFeatureSubcomponent { // 声明可注入的目标类 fun inject(fragment: ProfitFragment) fun inject(viewModel: ProfitViewModel) // 子组件工厂,用于从父组件创建子组件实例 @Subcomponent.Factory interface Factory { fun create(): ProfitFeatureSubcomponent } }
3. 编写功能依赖模块
在模块中提供ProfitStore,并使用自定义作用域标记,确保每个子组件实例拥有独立的ProfitStore:
@Module class ProfitFeatureModule { @ProfitFeatureScope @Provides fun provideProfitStore(): ProfitStore { return ProfitStore() } }
4. 在父组件中暴露子组件工厂
修改Application级别的Dagger组件,添加子组件工厂的获取方法,允许从全局组件创建功能子组件:
@Singleton @Component(modules = [AndroidInjectionModule::class, ...]) interface AppComponent { // 提供子组件工厂 fun profitFeatureSubcomponentFactory(): ProfitFeatureSubcomponent.Factory }
5. 管理子组件生命周期
创建管理器类,存储和管理不同功能实例对应的子组件,以功能的唯一标识(如标签ID、UUID)作为key:
class ProfitFeatureComponentManager(private val appComponent: AppComponent) { private val componentMap = mutableMapOf<String, ProfitFeatureSubcomponent>() // 根据功能ID获取或创建子组件 fun getOrCreateComponent(featureId: String): ProfitFeatureSubcomponent { return componentMap.getOrPut(featureId) { appComponent.profitFeatureSubcomponentFactory().create() } } // 销毁指定功能的子组件,避免内存泄漏 fun destroyComponent(featureId: String) { componentMap.remove(featureId) } }
在Application类中初始化这个管理器:
class MyApp : Application() { val appComponent: AppComponent by lazy { DaggerAppComponent.create() } val profitComponentManager by lazy { ProfitFeatureComponentManager(appComponent) } }
6. 在Fragment/ViewModel中注入依赖
在Fragment中,根据当前功能的唯一ID获取对应的子组件完成注入,并在Fragment销毁时清理子组件:
class ProfitFragment : Fragment() { private lateinit var profitStore: ProfitStore private lateinit var featureId: String override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) // 从 arguments 获取功能ID,或生成唯一UUID featureId = arguments?.getString("FEATURE_ID") ?: UUID.randomUUID().toString() val componentManager = (requireContext().applicationContext as MyApp).profitComponentManager val component = componentManager.getOrCreateComponent(featureId) component.inject(this) } override fun onDestroy() { super.onDestroy() // 当Fragment被移除时,销毁对应的子组件 if (isRemoving) { val componentManager = (requireContext().applicationContext as MyApp).profitComponentManager componentManager.destroyComponent(featureId) } } }
对于ViewModel,通过自定义ViewModelProvider.Factory注入依赖:
class ProfitViewModelFactory( private val featureId: String, private val componentManager: ProfitFeatureComponentManager ) : ViewModelProvider.Factory { @Suppress("UNCHECKED_CAST") override fun <T : ViewModel> create(modelClass: Class<T>): T { val component = componentManager.getOrCreateComponent(featureId) return ProfitViewModel(component.provideProfitStore()) as T } }
关键注意事项
- 功能唯一标识:必须确保每个功能实例有独一无二的ID,避免不同实例共享子组件。
- 内存泄漏防范:当功能实例被销毁(如标签关闭)时,一定要调用
destroyComponent移除对应的子组件,防止内存泄漏。 - 子组件与父组件的关系:子组件可以访问父组件(ApplicationComponent)提供的全局依赖,同时拥有自己的独立作用域依赖,兼顾共享与隔离。
内容的提问来源于stack exchange,提问作者jkistler
相关产品推荐
相关产品推荐

