如何基于Hilt实现多个ViewModel共享访问Room数据库
嘿,我来帮你搞定这个问题!你之前的写法里每个ViewModel都自己初始化Room实例,不仅重复代码还容易造成资源浪费,用Hilt来做依赖注入刚好能完美解决多ViewModel共享同一个Room数据库实例的问题,而且代码会更干净易维护,完全符合Android的最佳实践。咱们一步步来实现:
1. 先添加必要的依赖
首先得在项目里引入Hilt和Room的相关依赖,打开你模块下的build.gradle(注意是Module级别的,不是Project级的),添加这些内容:
// 顶部添加Hilt插件 plugins { id 'com.android.application' id 'org.jetbrains.kotlin.android' id 'kotlin-kapt' id 'dagger.hilt.android.plugin' } // 依赖部分 dependencies { // Hilt核心依赖 implementation "com.google.dagger:hilt-android:2.48" kapt "com.google.dagger:hilt-android-compiler:2.48" // Room依赖 implementation "androidx.room:room-runtime:2.6.1" implementation "androidx.room:room-ktx:2.6.1" kapt "androidx.room:room-compiler:2.6.1" // Room协程支持 implementation "androidx.room:room-coroutines:2.1.0-alpha01" // ViewModel和协程相关(如果还没加的话) implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.2" implementation "androidx.lifecycle:lifecycle-runtime-ktx:2.6.2" } // 配置kapt kapt { correctErrorTypes = true }
2. 配置Hilt的Application类
Hilt需要一个标注了@HiltAndroidApp的Application类作为入口,咱们创建一个:
@HiltAndroidApp class MyApp : Application() { // 这里不需要写额外代码,Hilt会自动初始化 }
别忘了在AndroidManifest.xml里把Application指定为这个类:
<application android:name=".MyApp" ...> ... </application>
3. 定义Room数据库和DAO
这部分和你原来的差不多,但要确保数据库是抽象类,DAO用@Dao注解:
// DAO接口 @Dao interface DataDao { @Query("SELECT * FROM data") suspend fun getAll(): List<Data> // 其他增删改查方法按需添加 @Insert(onConflict = OnConflictStrategy.REPLACE) suspend fun insertAll(dataList: List<Data>) } // 数据库类 @Database(entities = [Data::class], version = 1, exportSchema = false) abstract class AppDatabase : RoomDatabase() { abstract fun dataDao(): DataDao }
这里的Data就是你的数据实体类,记得加@Entity注解哦。
4. 创建Repository层(关键!)
咱们把数据库操作封装在Repository里,ViewModel只和Repository交互,这样解耦性更强,也符合MVVM的架构规范:
class DataRepository @Inject constructor( private val dataDao: DataDao ) { suspend fun getAllData(): List<Data> { return dataDao.getAll() } // 其他数据操作方法,比如插入、删除,都在这里封装 suspend fun insertData(dataList: List<Data>) { dataDao.insertAll(dataList) } }
5. 用Hilt提供数据库和Repository的实例
现在写一个Hilt的Module,用来告诉Hilt怎么创建AppDatabase和Repository的实例,并且保证它们是单例的:
@Module @InstallIn(SingletonComponent::class) // 绑定到SingletonComponent,整个应用生命周期只有一个实例 object DatabaseModule { @Provides @Singleton fun provideAppDatabase(@ApplicationContext context: Context): AppDatabase { return Room.databaseBuilder( context, AppDatabase::class.java, "database" ).build() } @Provides fun provideDataDao(appDatabase: AppDatabase): DataDao { return appDatabase.dataDao() } @Provides @Singleton fun provideDataRepository(dataDao: DataDao): DataRepository { return DataRepository(dataDao) } }
这里@Singleton注解保证了AppDatabase和Repository在整个应用中只有一个实例,所有ViewModel用的都是同一个数据库连接。
6. 在ViewModel里注入Repository
现在ViewModel不需要自己初始化数据库了,直接通过构造函数注入Repository,而且要用@HiltViewModel注解让Hilt能管理这个ViewModel:
@HiltViewModel class DataViewModel @Inject constructor( private val dataRepository: DataRepository ) : ViewModel() { private val _mutableDataFlow = MutableStateFlow<List<Data>>(emptyList()) val data: StateFlow<List<Data>> = _mutableDataFlow.asStateFlow() fun loadData() { viewModelScope.launch { _mutableDataFlow.value = dataRepository.getAllData() } } // 其他操作数据的方法,比如插入数据 fun insertData(dataList: List<Data>) { viewModelScope.launch { dataRepository.insertData(dataList) } } }
如果有其他ViewModel(比如另一个OtherViewModel),只需要同样用@HiltViewModel注解,构造函数注入DataRepository(或者对应其他数据的Repository)就可以了,完全不需要重复初始化数据库!
7. 在Activity/Fragment中使用ViewModel
最后,在Activity或者Fragment里,用Hilt的方式获取ViewModel,直接用by viewModels()就可以,Hilt会自动把依赖注入进去:
// 记得Activity要加@AndroidEntryPoint注解,Hilt才能注入 @AndroidEntryPoint class MainActivity : AppCompatActivity() { private val dataViewModel: DataViewModel by viewModels() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) // 观察数据 lifecycleScope.launch { repeatOnLifecycle(Lifecycle.State.STARTED) { dataViewModel.data.collect { dataList -> // 更新UI } } } // 加载数据 dataViewModel.loadData() } }
Fragment的话同样要加@AndroidEntryPoint注解,然后用by viewModels()获取ViewModel。
总结一下
这样实现之后,所有的ViewModel都会共享同一个Room数据库实例,没有重复的数据库初始化代码,依赖注入让代码解耦,也符合Android的最佳实践。以后再新增ViewModel,只需要注入对应的Repository就行,非常方便。
内容来源于stack exchange

