使用Hilt实现带运行时参数与Repository依赖的ViewModel注入时遇到禁止注入Assisted Factory的错误
看起来你遇到了Hilt辅助注入ViewModel时的常见坑——直接注入Assisted Factory到ViewModelComponent导致的禁止错误,我来帮你梳理问题和解决办法:
错误原因分析
你收到的错误提示核心是:Hilt不允许在ViewModelComponent中注入ViewModel的Assisted Factory,因为ViewModel的创建必须遵循Android的ViewModelProvider生命周期管理机制,直接从ViewModelComponent获取Factory会破坏ViewModel的生命周期绑定逻辑,而且会导致循环依赖(ViewModel的创建依赖Factory,而Factory又依赖ViewModelComponent)。
你的代码里有两个关键问题:
ViewModelFactoryProvider这个EntryPoint被错误地安装在了ViewModelComponent里- 你试图在
ViewModelModule中手动提供ViewModelProvider.Factory,这完全没必要,Hilt已经帮你处理了Assisted Factory的生成逻辑
正确的实现步骤
1. 保留ViewModel的正确写法(这部分你是对的)
你的ViewModel使用@HiltViewModel(assistedFactory = ...)和@AssistedInject的写法完全符合Hilt规范,不需要修改:
@HiltViewModel(assistedFactory = DetailViewModel.Factory::class) class DetailViewModel @AssistedInject constructor( private val repository: Repository, @Assisted itemId: Int ) : ViewModel() { // 你的业务逻辑... @AssistedFactory interface Factory { fun create(itemId: Int): DetailViewModel } }
2. 修正EntryPoint的安装组件
把获取Assisted Factory的EntryPoint安装在SingletonComponent(或者ActivityRetainedComponent)里,这样我们可以在Activity/Composable层面提前获取到Factory实例:
@EntryPoint @InstallIn(SingletonComponent::class) interface ViewModelFactoryProvider { fun detailViewModelFactory(): DetailViewModel.Factory }
3. 移除ViewModelModule中多余的Factory提供代码
删除ViewModelModule里的provideDetailViewModelFactory方法,因为我们不需要手动创建ViewModelProvider.Factory,而是在使用时动态生成:
@Module @InstallIn(ViewModelComponent::class) object ViewModelModule { @Provides fun provideRepository(listDao: ShoppingListDao,itemDao: ItemDao,storeDao: StoreDao): Repository { return Repository(listDao,itemDao,storeDao) } // 删掉下面这段多余的代码 // @Provides // fun provideDetailViewModelFactory(factory: DetailViewModel.Factory, itemId: Int): ViewModelProvider.Factory { // ... // } }
4. 在Composable中正确创建ViewModel
在Composable里,通过Application上下文获取Assisted Factory,然后动态创建ViewModelProvider.Factory并传入运行时的id参数:
@Composable fun DetailScreen( id: Int, navigateUp: () -> Unit ) { val applicationContext = LocalContext.current.applicationContext // 通过EntryPoint获取Hilt自动生成的Assisted Factory val assistedFactory = EntryPointAccessors.fromApplication( applicationContext, ViewModelFactoryProvider::class.java ).detailViewModelFactory() // 动态创建ViewModelFactory,传入运行时参数id val viewModelFactory = object : ViewModelProvider.Factory { override fun <T : ViewModel> create(modelClass: Class<T>): T { return assistedFactory.create(id) as T } } // 通过viewModel函数绑定ViewModel生命周期 val viewModel: DetailViewModel = viewModel(factory = viewModelFactory) // 你的UI逻辑... }
为什么这么做?
- 从SingletonComponent获取Assisted Factory,确保在ViewModel创建之前就能拿到实例,避免了循环依赖
- 动态创建
ViewModelProvider.Factory既可以灵活传入运行时参数,又能保留Hilt对Repository等依赖的自动注入能力 - 完全遵循Android ViewModel的生命周期管理机制,确保ViewModel在配置变化时不会被意外销毁
备注:内容来源于stack exchange,提问作者Fehru Mandala Putra

