如何在非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.
相关产品推荐
相关产品推荐

