Android Hilt中数据库重下后实例引用更新问题求助
核心问题分析
Hilt会缓存SingletonComponent中提供的依赖实例(即使未显式标注@Singleton,Scoped组件内的无作用域绑定也可能被缓存),导致数据库文件替换后,已注入的ItemDao仍引用旧数据库实例。同时,ViewModel等Scoped组件会缓存注入的依赖,进一步加剧旧实例的残留问题。
解决方案
方案1:注入动态Dao提供者(推荐)
通过封装一个Dao获取类,每次使用时都从最新的数据库实例中获取Dao:
- 创建Dao提供者类
class ItemDaoProvider { fun get(): ItemDao { return ProjectDatabase.getDatabase().roomItemDao() } }
- 修改Hilt Module
@Module @InstallIn(SingletonComponent::class) class ProjectDatabaseModule { @Provides fun provideItemDaoProvider(): ItemDaoProvider { return ItemDaoProvider() } }
- 在依赖类中使用提供者
class InventoryViewModel @ViewModelInject constructor( private val itemDaoProvider: ItemDaoProvider ) : ViewModel() { fun queryItem(scanCode: String) { // 每次调用都获取最新的Dao实例 val itemDao = itemDaoProvider.get() viewModelScope.launch { val item = itemDao.getItemByCode(scanCode) // 处理查询结果 } } }
方案2:使用Hilt内置Provider接口
利用Hilt的Provider接口实现动态实例获取:
- 修改Hilt Module
@Module @InstallIn(SingletonComponent::class) class ProjectDatabaseModule { @Provides fun provideProjectDatabaseProvider(): Provider<ProjectDatabase> { return Provider { ProjectDatabase.getDatabase() } } @Provides fun provideItemDaoProvider(dbProvider: Provider<ProjectDatabase>): Provider<ItemDao> { return Provider { dbProvider.get().roomItemDao() } } }
- 在依赖类中使用
Provider
class InventoryViewModel @ViewModelInject constructor( private val itemDaoProvider: Provider<ItemDao> ) : ViewModel() { fun queryItem(scanCode: String) { viewModelScope.launch { val item = itemDaoProvider.get().getItemByCode(scanCode) // 处理查询结果 } } }
关键注意事项
- 严格执行数据库替换流程:
- 先停止所有数据库操作(如取消协程、禁用UI交互)
- 调用
ProjectDatabase.closeDatabase()关闭旧实例并置空INSTANCE - 删除/覆盖旧数据库文件
- 下载新数据库文件到指定路径
- 后续操作通过动态提供者获取新Dao实例
- 避免单例类直接注入Dao:单例类会长期持有旧实例,必须改用动态提供者方式获取Dao
- 修正Room初始化错误:原代码中
createFromAsset(getDbNameWithPath())逻辑错误(getDbNameWithPath()指向filesDir路径,而非assets目录),应改为createFromFile(File(getDbNameWithPath())),确保从本地文件加载数据库
内容的提问来源于stack exchange,提问作者Péter Balogh
相关产品推荐
相关产品推荐

