You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.26 14:42:41