MVVM架构下使用Hilt注入ViewModel遇绑定错误,求解决方案
问题:Hilt实现ViewModel依赖注入报错的解决方法
背景
APP采用MVVM架构,每个调用API的页面都存在重复代码:
val retrofitService = RetrofitService.getInstance(requireContext()) val mainRepository = MainRepository(retrofitService) val viewModel = ViewModelProvider( viewOwner, AppVMFactory(mainRepository) )[AppViewModel::class.java]
为减少重复引入Hilt,已添加相关依赖、创建Application类与AppModule:
@Module @InstallIn(SingletonComponent::class) object AppModule{ @Singleton fun provideApp(@ApplicationContext app: Context):BaseApplication = app as BaseApplication @Singleton @Provides fun someStr() = "Hello World from DI" }
测试Activity成功注入字符串,但添加AppViewModel的提供方法后构建报错:
@Module @InstallIn(SingletonComponent::class) object AppModule{ .... @Singleton @Provides fun vmSupport(context: Context, owner: ViewModelStoreOwner): AppViewModel { val retrofitService = RetrofitService.getInstance(context) val mainRepository = MainRepository(retrofitService) return ViewModelProvider( owner, AppVMFactory(mainRepository) )[AppViewModel::class.java] } .... }
错误信息:
[Dagger/MissingBinding] android.content.Context cannot be provided without an @Provides-annotated method. public abstract static class SingletonC implements HiltWrapper_ActivityRetainedComponentManager_ActivityRetainedComponentBuilderEntryPoint, ^ android.content.Context is injected at pkg.di.AppModule.vmSupport(context, …) pkg.viewModel.AppViewModel is injected at pkg.HomeScreen.vm pkg.HomeScreen is injected at pkg.HomeScreen_GeneratedInjector.injectHomeScreen(pkg.HomeScreen) [pkg.di.BaseApplication_HiltComponents.SingletonC → pkg.di.BaseApplication_HiltComponents.ActivityRetainedC → pkg.di.BaseApplication_HiltComponents.ActivityC]
Activity中使用@Inject lateinit var vm:AppViewModel注入,推测是参数传递问题,但不清楚正确实现方式。
相关类代码:
- AppViewModel:
class AppViewModel(private val mainRepository: MainRepository) : ViewModel(){...}
- MainRepository:
class MainRepository constructor(private val retrofitService: RetrofitService){...}
- AppVMFactory:
class AppVMFactory(private val repository: MainRepository) : ViewModelProvider.Factory { override fun <T : ViewModel> create(modelClass: Class<T>): T { return if (modelClass.isAssignableFrom(AppViewModel::class.java)) { AppViewModel(this.repository) as T } else { throw IllegalArgumentException("ViewModel Not Found") } } }
错误原因
直接在SingletonComponent的Module中提供ViewModel实例的做法完全错误:
- ViewModel的生命周期由
ViewModelStoreOwner(Activity/Fragment)管理,不能声明为@Singleton,否则会打破ViewModel的生命周期绑定规则。 ViewModelStoreOwner属于Activity/Fragment层级组件,无法在全局单例的SingletonComponent中注入,两者生命周期不匹配。Context参数未添加@ApplicationContext注解,Hilt无法识别要注入的全局上下文实例。
正确实现步骤
1. 配置Hilt ViewModel依赖
确保已添加Hilt对ViewModel的支持依赖(若未添加):
implementation "com.google.dagger:hilt-android:2.48" kapt "com.google.dagger:hilt-android-compiler:2.48" implementation "androidx.hilt:hilt-lifecycle-viewmodel:1.0.0-alpha03" kapt "androidx.hilt:hilt-compiler:1.0.0"
2. 改造ViewModel,添加Hilt注解
给AppViewModel添加@HiltViewModel注解,并让构造函数使用@Inject标记:
import androidx.lifecycle.ViewModel import dagger.hilt.android.lifecycle.HiltViewModel @HiltViewModel class AppViewModel @Inject constructor(private val mainRepository: MainRepository) : ViewModel(){ // 原有业务逻辑 }
注:新版本Hilt已弃用
ViewModelInject,直接使用@Inject即可。
3. 在AppModule中提供底层依赖
只需要提供RetrofitService和MainRepository的实例,无需处理ViewModel的创建:
@Module @InstallIn(SingletonComponent::class) object AppModule{ @Singleton @Provides fun provideRetrofitService(@ApplicationContext context: Context): RetrofitService { return RetrofitService.getInstance(context) } @Singleton @Provides fun provideMainRepository(retrofitService: RetrofitService): MainRepository { return MainRepository(retrofitService) } // 原有其他提供方法 @Singleton @Provides fun someStr() = "Hello World from DI" }
4. 在Activity/Fragment中注入ViewModel
使用by viewModels()委托获取ViewModel(需导入Hilt的ViewModel扩展):
import androidx.activity.viewModels import dagger.hilt.android.AndroidEntryPoint @AndroidEntryPoint class HomeScreen : AppCompatActivity() { // Hilt自动处理依赖注入,直接获取ViewModel private val vm: AppViewModel by viewModels() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) // 正常使用vm即可 } }
Fragment同样需要添加@AndroidEntryPoint,根据需求选择by viewModels()(Fragment独立ViewModel)或by activityViewModels()(共享Activity的ViewModel)。
5. 废弃自定义ViewModelFactory
使用Hilt后,无需自己实现AppVMFactory,Hilt会自动生成对应的工厂类处理依赖注入逻辑。
关键说明
@HiltViewModel是Hilt识别ViewModel的核心标记,配合构造函数的@Inject,Hilt会自动将所需依赖注入到ViewModel中。@AndroidEntryPoint必须添加到需要注入依赖的Activity/Fragment上,Hilt才能生成对应的注入代码。- ViewModel的生命周期与宿主绑定,绝不能在Singleton组件中提供,避免生命周期冲突导致的内存泄漏或数据异常。
内容的提问来源于stack exchange,提问作者Andrew
相关产品推荐
相关产品推荐

