如何通过Dagger2将Fragment的deviceType注入其作用域的FooViewModel?
我有一个作用域限定为FooFragment的FooViewModel,FooFragment中的deviceType是根据用户选择从另一个Fragment传入的。我希望将该deviceType注入FooViewModel的构造函数,请问应如何在Module中提供deviceType?
我的Module配置如下:
@Module interface FooModule { @ContributesAndroidInjector fun contributesFooFragment(): FooFragment companion object { @Provides fun provideDeviceType(fragment: FooFragment) = fragment.deviceType } }
运行时遇到以下错误:
[Dagger/MissingBinding] 无法提供FooFragment,因为没有@Inject构造函数或带@Provides注解的方法。该类型支持成员注入,但无法隐式提供。
...
问题原因
你在provideDeviceType方法中直接依赖FooFragment实例,但Dagger在当前Module的作用域逻辑里,无法自动获取到FooFragment的实例——@ContributesAndroidInjector负责生成Fragment的注入器,但同一Module伴生对象中的依赖提供逻辑,无法直接关联到Fragment的实例上下文。
解决方案
方案一:利用ViewModel辅助注入(推荐,适配动态参数场景)
动态参数更适合通过AssistedInject配合ViewModel工厂传递,避免Dagger作用域冲突:
- 给
FooViewModel定义带动态参数的构造,并用@Assisted标记动态参数:
class FooViewModel @Inject constructor( @Assisted private val deviceType: String, // 其他Dagger可注入的依赖 ) : ViewModel() { // ViewModel逻辑 }
- 创建辅助工厂接口和ViewModel工厂:
// 辅助工厂,用于生成带动态参数的ViewModel @AssistedFactory interface FooViewModelAssistedFactory { fun create(deviceType: String): FooViewModel } // 实际提供给Fragment的ViewModel工厂 class FooViewModelFactory @Inject constructor( private val assistedFactory: FooViewModelAssistedFactory, private val deviceType: String ) : ViewModelProvider.Factory { override fun <T : ViewModel> create(modelClass: Class<T>): T { return assistedFactory.create(deviceType) as T } }
(需要添加dagger-android-assisted-inject依赖到项目中)
- 在
FooFragment中传入动态参数并获取ViewModel:
class FooFragment : Fragment() { private lateinit var deviceType: String @Inject lateinit var viewModelFactoryProvider: Provider<FooViewModelFactory> private val viewModel: FooViewModel by viewModels { viewModelFactoryProvider.get().apply { deviceType = this@FooFragment.deviceType } } override fun onCreate(savedInstanceState: Bundle?) { // 先从参数中拿到deviceType deviceType = arguments?.getString("KEY_DEVICE_TYPE") ?: "" // 再执行Dagger注入 AndroidSupportInjection.inject(this) super.onCreate(savedInstanceState) } }
方案二:调整Dagger Module的作用域结构
如果坚持用Dagger直接注入,需要确保Fragment实例能在作用域内被正确提供:
- 将依赖提供逻辑移到Fragment的子Module中,并限定作用域:
@Module interface FooModule { @ContributesAndroidInjector(modules = [FooFragmentInternalModule::class]) fun contributesFooFragment(): FooFragment } @Module class FooFragmentInternalModule { @Provides @FragmentScoped fun provideDeviceType(fragment: FooFragment): String { return fragment.deviceType } }
- 给
FooViewModel添加Fragment级作用域注解:
@FragmentScoped class FooViewModel @Inject constructor( private val deviceType: String ) : ViewModel() { // ViewModel逻辑 }
注意:这种方式要求deviceType必须在Dagger注入Fragment之前完成赋值(比如在AndroidSupportInjection.inject(this)之前从arguments中取出并赋值),否则注入时会拿到空值。
内容的提问来源于stack exchange,提问作者SpeedRacer

