如何用Dagger2/Hilt将Room数据库单例注入所有ViewModel
Dagger2单例注入Room到ViewModel+ Hilt错误修复指南
一、Dagger2相关问题解答
1. Dagger2比直接传Application实例好在哪?
- 解耦彻底:ViewModel不用关心Room实例的创建逻辑,只需要声明依赖即可。后续更换数据库实现(比如换成Realm)时,ViewModel代码无需改动,仅调整Dagger配置就能完成切换。
- 生命周期可控:Dagger能精准管理单例的生命周期(比如Application级全局单例),避免手动管理实例时出现的内存泄漏、重复创建等问题。
- 测试便捷:编写单元测试时,可直接为ViewModel注入Mock版本的Room实例,无需修改ViewModel核心代码。
- 自动处理依赖链:面对复杂依赖关系(比如Room依赖Context、Repository依赖Room、ViewModel依赖Repository),无需手动层层传递,Dagger会自动构建完整的依赖树。
2. Fragment传AppComponent给ViewModelFactory的优化方式
别手动传递AppComponent了,这么改更简洁:
- 编写被Dagger注入的ViewModelFactory:
class AppViewModelFactory @Inject constructor( private val userRepo: UserRepository // 直接注入Room相关的Repository ) : ViewModelProvider.Factory { override fun <T : ViewModel> create(modelClass: Class<T>): T { return when (modelClass) { RegistrationViewModel::class.java -> RegistrationViewModel(userRepo) // 其他ViewModel同理 else -> throw IllegalArgumentException("未识别的ViewModel类") } as T } } - 在AppComponent中暴露Factory:
@Component(modules = [AppModule::class]) interface AppComponent { fun viewModelFactory(): AppViewModelFactory // 其他需要暴露的依赖 } - Fragment中直接获取Factory:
这样无需手动传递多个依赖,仅通过Factory就能完成ViewModel的实例化。class RegistrationFragment : Fragment() { private lateinit var viewModel: RegistrationViewModel override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) val appComponent = (requireActivity().application as MyApp).appComponent val factory = appComponent.viewModelFactory() viewModel = ViewModelProvider(this, factory)[RegistrationViewModel::class.java] } }
二、Hilt报错“@InstallIn仅可用于@DefineComponent注解类”的修复
这个错误大多是依赖版本不匹配或注解使用错误导致的,按以下步骤排查:
1. 检查Hilt依赖版本是否统一
所有Hilt相关依赖版本必须一致,避免版本混乱。比如项目根目录的build.gradle:
plugins { id 'com.android.application' version '7.4.2' apply false id 'com.google.dagger.hilt.android' version '2.44' apply false }
模块级build.gradle:
plugins { id 'com.android.application' id 'com.google.dagger.hilt.android' } dependencies { implementation 'com.google.dagger:hilt-android:2.44' kapt 'com.google.dagger:hilt-compiler:2.44' // ViewModel的Hilt依赖 implementation 'androidx.hilt:hilt-lifecycle-viewmodel:1.0.0-alpha03' kapt 'androidx.hilt:hilt-compiler:1.0.0' }
注意hilt-lifecycle-viewmodel与hilt-compiler版本需兼容,直接使用官方推荐的匹配版本即可。
2. 检查注解使用是否正确
- 你的Module类必须同时使用
@Module和@InstallIn注解,且@InstallIn指定的是Hilt自带的Component,比如:@Module @InstallIn(SingletonComponent::class) // 只能是Hilt提供的Component,如SingletonComponent、ActivityComponent等 object RoomModule { @Provides @Singleton fun provideAppDatabase(@ApplicationContext context: Context): AppDatabase { return Room.databaseBuilder( context, AppDatabase::class.java, "app_db" ).build() } @Provides fun provideUserDao(db: AppDatabase): UserDao { return db.userDao() } } - 不要随意自定义
@DefineComponent,普通开发场景使用Hilt自带的Component即可。
3. 检查Application类的注解
你的Application类必须添加@HiltAndroidApp注解:
@HiltAndroidApp class MyApp : Application() { // ... }
4. 清理缓存并重新构建
缓存异常也可能导致此类问题,执行File -> Invalidate Caches... -> Invalidate and Restart,然后重新编译项目即可。
三、Hilt注入Room到ViewModel的正确流程
- 按上述步骤配置好Hilt依赖和Module。
- ViewModel使用
@HiltViewModel+构造函数注入:@HiltViewModel class RegistrationViewModel @Inject constructor( private val userDao: UserDao ) : ViewModel() { // 业务逻辑编写在此 } - Fragment添加
@AndroidEntryPoint注解,直接获取ViewModel:
Hilt会自动完成依赖注入,无需手动处理Factory相关逻辑。@AndroidEntryPoint class RegistrationFragment : Fragment() { private val viewModel: RegistrationViewModel by viewModels() override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) // 直接使用viewModel即可 } }
内容的提问来源于stack exchange,提问作者Martin
相关产品推荐
相关产品推荐

