首次实例化后如何通过Hilt注入重新创建或替换Room数据库?
问题根因
你当前的Hilt数据库实例标注了@Singleton注解,Hilt会全局缓存首次创建的数据库实例,后续所有依赖注入的DAO、数据库实例都会复用这个旧对象。你手动创建的新数据库实例没有替换Hilt的全局缓存,因此所有数据库操作还是走的旧连接,返回旧数据。
解决方案
通过自定义持有类接管数据库实例的管理,支持主动替换实例:
- 新增数据库持有类,用来存储可更新的数据库实例,同时封装旧库关闭逻辑
@Singleton class DatabaseHolder { @Volatile var instance: LocalDB? = null fun closeOldInstance() { instance?.close() instance = null } }
- 修改Hilt DatabaseModule,改为从持有类中提供数据库实例
@Module @InstallIn(SingletonComponent::class) class DatabaseModule { // 提供全局单例的数据库持有类 @Singleton @Provides fun provideDatabaseHolder() = DatabaseHolder() // 数据库实例从持有类中获取,首次访问自动初始化默认库 @Provides fun provideLocalDB( @ApplicationContext context: Context, holder: DatabaseHolder ): LocalDB { return holder.instance ?: synchronized(holder) { holder.instance ?: Room.databaseBuilder( context, LocalDB::class.java, "local.db" ).build().also { holder.instance = it } } } }
- 实现数据库替换逻辑,完成旧库关闭、新库初始化、实例替换全流程
suspend fun updateDatabase(context: Context, holder: DatabaseHolder) { withContext(Dispatchers.IO) { // 1. 关闭旧数据库连接,释放文件锁 holder.closeOldInstance() // 2. 删除原有数据库文件,避免旧数据残留 context.deleteDatabase("local.db") // 3. 初始化新数据库实例 val newDB = Room.databaseBuilder( context, LocalDB::class.java, "local.db" ) .createFromAsset("new.sqlite") .fallbackToDestructiveMigration() // 如果新库版本低于旧库,添加下面这行配置 // .fallbackToDestructiveMigrationOnDowngrade() .build() // 4. 更新持有类的实例,后续所有注入的数据库都会使用新实例 holder.instance = newDB } }
如果是替换服务端下载的Sqlite文件,将createFromAsset改为createFromFile,传入下载好的Sqlite文件路径即可。
注意事项
- 数据库替换逻辑必须在子线程执行,禁止在主线程操作
- 替换过程中需要暂停所有数据库读写操作,避免出现读写异常,可自行添加全局状态位控制访问权限
- 替换完成后无需重启应用,后续所有通过Hilt注入的数据库依赖都会自动使用新实例
内容的提问来源于stack exchange,提问作者nifira
相关产品推荐
相关产品推荐

