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

如何在非SessionScope的ViewModel中注入Dagger2自定义Scope依赖?

解决Dagger跨Scope注入ViewModel依赖的问题

你遇到的核心问题是:Singleton Scope的AppComponent无法访问Session Scope子组件的依赖——Dagger子组件的依赖默认不会向上暴露给父组件,而ViewModel由AppComponent管理的Factory创建,自然找不到SessionScope的TestInjectClass。以下是几种无需将ViewModel标记为SessionScope的解决方案:

方法一:通过ViewModelFactory注入SessionComponent获取依赖

  • 修改ViewModelModule,让ViewModelFactory的构造函数注入SessionComponent(需确保AppComponent能提供SessionComponent实例,比如在AppComponent中声明SessionComponent sessionComponent()方法)
  • 在ViewModelFactory的create方法中,实例化MyViewModelImpl时,从SessionComponent中获取TestInjectClass实例并手动传入构造函数:
    class ViewModelFactory @Inject constructor(
        private val sessionComponent: SessionComponent,
        private val viewModels: Map<Class<out ViewModel>, @JvmSuppressWildcards Provider<ViewModel>>
    ) : ViewModelProvider.Factory {
        override fun <T : ViewModel> create(modelClass: Class<T>): T {
            val provider = viewModels[modelClass] ?: throw IllegalArgumentException("Unknown ViewModel class")
            // 针对MyViewModelImpl单独处理,传入SessionScope的依赖
            return if (modelClass == MyViewModelImpl::class.java) {
                MyViewModelImpl(sessionComponent.testInjectClass) as T
            } else {
                provider.get() as T
            }
        }
    }
    
  • 这种方式下,ViewModel依然由AppComponent管理,仅在创建时通过SessionComponent获取跨Scope依赖。

方法二:在AppComponent中暴露SessionScope依赖的获取路径

  • 在SessionComponent中定义暴露TestInjectClass的方法:
    @SessionScope
    @Subcomponent(modules = [SessionModule::class])
    interface SessionComponent {
        fun getTestInjectClass(): TestInjectClass
        // 其他子组件方法...
    }
    
  • 在AppComponent中添加获取SessionComponent的方法:
    @Singleton
    @Component(modules = [ViewModelModule::class, AppSubComponents::class])
    interface AppComponent {
        fun sessionComponent(): SessionComponent.Builder
        // 其他AppComponent方法...
    }
    
  • 在AppModule中添加无Scope的@Provides方法,转发SessionComponent的依赖:
    @Module
    class AppModule {
        @Provides
        fun provideTestInjectClass(appComponent: AppComponent): TestInjectClass {
            // 需确保SessionComponent已正确初始化(如用户登录后创建)
            return appComponent.sessionComponent().build().getTestInjectClass()
        }
    }
    
  • 这样AppComponent就能通过转发路径找到TestInjectClass,ViewModel的构造注入即可正常工作。

方法三:手动管理SessionScope实例并绑定到AppComponent

  • 如果TestInjectClass的实例可在Session生命周期内手动创建(比如登录成功后初始化),修改AppComponent的Builder,通过@BindsInstance传入实例:
    @Singleton
    @Component(modules = [ViewModelModule::class])
    interface AppComponent {
        fun inject(fragment: MyFragment)
        
        @Component.Builder
        interface Builder {
            @BindsInstance
            fun testInjectClass(testInjectClass: TestInjectClass): Builder
            fun build(): AppComponent
        }
    }
    
  • 在Session开始时(如登录)创建TestInjectClass实例并更新AppComponent;Session结束时(如登出)销毁实例并重建AppComponent。
  • 这种方式无需依赖子组件,直接让AppComponent持有SessionScope对象,ViewModel可直接通过构造注入获取。

注意事项

  • 无论哪种方案,都要严格管理TestInjectClass的生命周期,确保Session结束时及时销毁实例,避免内存泄漏。
  • 方法二中的@Provides方法不能标记任何Scope,否则会与SessionScope的规则冲突。

内容的提问来源于stack exchange,提问作者Klitos G.

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.21 16:52:34