ViewModelFactory与AndroidInjection的Dagger循环依赖根因问询
Dagger循环依赖问题分析:Fragment与Activity场景的差异
问题背景
在Fragment中注入ViewModelFactory时触发Dagger循环依赖错误,但相同的注入逻辑在Activity中却无此问题。已通过添加@Named("machine-vm-factory")注解解决问题,需分析根本原因。
相关代码片段
出现循环依赖的Fragment代码
class MachineFragment : Fragment() { @Inject lateinit var viewModelFactory: ViewModelFactory private lateinit var viewModel: MachinesViewModel override fun onCreate(savedInstanceState: Bundle?) { AndroidSupportInjection.inject(this) super.onCreate(savedInstanceState) } override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View? { super.onCreateView(inflater, container, savedInstanceState) viewModel = ViewModelProvider(this, viewModelFactory).get(MachinesViewModel::class.java) return inflater.inflate(R.layout.fragment_machines, container, false) } }
AppComponent定义
@Singleton @Component( dependencies = [], modules = [ AppModule::class, TestViewModelModule::class, InjectorModule::class ] ) interface AppComponent { fun inject(subject: AppApplication) }
TestViewModelModule定义
public abstract class TestViewModelModule { @Binds public abstract ViewModelFactory bindViewModelFactory(ViewModelFactory viewModelFactory); /* you have to explicitly define ViewModel instead of concrete implementation to avoid cycle dependency error */ @Binds @IntoMap @ViewModelKey(MachinesViewModel.class) abstract ViewModel bindMachinesViewModel(MachinesViewModel vm); }
ViewModelFactory实现
public class ViewModelFactory implements ViewModelProvider.Factory { private final Map<Class<? extends ViewModel>, Provider<ViewModel>> viewModels; @Inject public ViewModelFactory(Map<Class<? extends ViewModel>, Provider<ViewModel>> viewModels) { this.viewModels = viewModels; } @Override public <T extends ViewModel> T create(Class<T> modelClass) { Provider<ViewModel> viewModelProvider = viewModels.get(modelClass); if (viewModelProvider == null) { throw new IllegalArgumentException("model class " + modelClass + " not found"); } return (T) viewModelProvider.get(); } }
无循环依赖的Activity代码
class MainActivity : AppCompatActivity() { @Inject lateinit var viewModelFactory: ViewModelFactory private lateinit var viewModel: MachinesViewModel override fun onCreate(savedInstanceState: Bundle?) { AndroidInjection.inject(this) super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) /* start destination in navigation.xml defines entry point */ viewModel = ViewModelProvider(this, viewModelFactory).get(MachinesViewModel::class.java) } }
已解决方法
通过在ViewModelFactory的绑定方法和注入字段上添加@Named("machine-vm-factory")注解,消除了循环依赖问题。
根本原因分析
1. 冗余绑定引发的依赖歧义
你的TestViewModelModule中存在一个无意义的冗余绑定:
@Binds public abstract ViewModelFactory bindViewModelFactory(ViewModelFactory viewModelFactory);
ViewModelFactory本身已经通过@Inject构造函数提供了实例,重复的@Binds声明会让Dagger在解析依赖时产生歧义:
- Activity场景:Activity是顶级注入节点,Dagger会优先选择构造函数注入的
ViewModelFactory实例,跳过冗余的@Binds绑定,因此不会触发循环。 - Fragment场景:Fragment的注入依赖于嵌套的Subcomponent(关联父Activity的Component),Subcomponent会继承父Component的所有绑定。此时冗余的
@Binds绑定会与构造函数注入的实例形成双重候选,Dagger会尝试通过@Binds链路解析依赖,而该链路又依赖包含MachinesViewModel的Map;如果MachinesViewModel的创建隐式依赖Fragment的上下文或其Subcomponent,就会形成闭环:Fragment → ViewModelFactory (via @Binds) → Map → MachinesViewModel → Fragment/其Subcomponent
2. 注入层级的差异
- Activity的注入直接关联顶级Component(如AppComponent),依赖链路扁平,Dagger解析时不会触发嵌套Component的循环引用。
- Fragment的注入依赖于Subcomponent,Subcomponent的生命周期与Fragment绑定,当依赖链路出现交叉引用时,更容易触发Dagger的循环依赖检测。
3. @Named注解的作用
添加@Named注解后,为ViewModelFactory的绑定添加了唯一标识,强制Dagger使用明确的命名实例,消除了冗余绑定带来的歧义,同时切断了闭环依赖链路,因此循环依赖问题得以解决。
内容的提问来源于stack exchange,提问作者Gleichmut
相关产品推荐
相关产品推荐

