KOIN重新装配依赖图时ViewModel实例未刷新问题求助
我之前也碰到过一模一样的困扰!问题的核心其实很好理解:ViewModel的生命周期是和Activity/Fragment的ViewModelStore绑定的,而不是直接绑定到Koin上下文。哪怕你销毁并重建了Koin的依赖图,只要Activity的ViewModelStore里还存着旧的ViewModel实例,系统就会直接复用它,不会去Koin里重新获取新的依赖。
下面是我试过的几个可行方案,按推荐程度排序:
方案1:用Koin Scope管理ViewModel(最符合Koin设计)
这个方案的思路是把ViewModel绑定到一个自定义的Koin Scope,而不是默认和Activity绑定的viewModelScope。当你重建Koin上下文时,先关闭这个自定义Scope,新的Koin实例会创建新的Scope,自然就能生成新的ViewModel实例。
步骤1:修改ViewModelModule,绑定到自定义Scope
object ViewModelModule { val module by lazy { module { // 定义一个命名Scope,用来管理需要刷新的ViewModel scope(named("app_runtime_scope")) { viewModel { AppLauncherViewModel(get(), get(), get(), get()) } viewModel { AuthLoginPasswordViewModel(get(), get()) } viewModel { SettingsViewModel(get(), get()) } // 其他需要刷新的ViewModel都放到这个Scope里 } } } }
步骤2:装配依赖时激活Scope
private fun assembleGraph() { val graph = listOf( AppModule.module, StorageModule.module, DatabaseConfigModule.module, RepositoryModule.module, InteractorModule.module, ViewModelModule.module ) application.startKoin(application, graph) // 创建并激活自定义Scope getKoin().createScope("app_runtime_scope", named("app_runtime_scope")) }
步骤3:重建依赖图时先关闭Scope
fun reassembleGraph() { // 先关闭旧的Scope,确保所有关联的实例被销毁 getKoin().getScopeOrNull("app_runtime_scope")?.close() stopKoin() assembleGraph() }
步骤4:从自定义Scope获取ViewModel
在Activity/Fragment里,不再用默认的by viewModel()委托,而是从自定义Scope获取:
val viewModel: AppLauncherViewModel by lazy { getKoin().getScope("app_runtime_scope") .getViewModel(this, AppLauncherViewModel::class.java) }
方案2:自定义ViewModelFactory(灵活但需要修改获取方式)
如果不想改动Scope的设计,可以自定义一个ViewModelFactory,每次创建ViewModel时都从当前的Koin上下文获取依赖,而不是复用旧的ViewModel实例。
实现自定义Factory
class ReassembleableKoinViewModelFactory( private val koin: Koin = GlobalContext.get() ) : ViewModelProvider.Factory { override fun <T : ViewModel> create(modelClass: Class<T>): T { // 每次创建都从当前Koin实例解析依赖 return koin.getViewModel(modelClass) } }
在Activity里使用这个Factory获取ViewModel
val viewModel = ViewModelProvider(this, ReassembleableKoinViewModelFactory()) .get(AppLauncherViewModel::class.java)
这样每次Koin重建后,新的Factory会使用新的Koin上下文来创建ViewModel,自然就能拿到刷新后的依赖。
方案3:手动清空ViewModelStore(简单但有风险)
这个方案比较直接,就是在重建Koin后,手动清空Activity的ViewModelStore,让系统下次获取ViewModel时重新创建。不过需要用到反射,可能会因为Android版本更新出现兼容性问题,谨慎使用。
实现清空方法
fun Activity.clearViewModelStore() { val viewModelStore = this.viewModelStore // 反射获取ViewModelStore里的mMap字段(存储ViewModel实例的容器) val mapField = ViewModelStore::class.java.getDeclaredField("mMap") mapField.isAccessible = true @Suppress("UNCHECKED_CAST") val viewModelMap = mapField.get(viewModelStore) as MutableMap<String, ViewModel> // 清空所有旧的ViewModel实例 viewModelMap.clear() }
调用时机
在reassembleGraph()之后调用:
fun reassembleGraph(activity: Activity) { stopKoin() assembleGraph() // 清空Activity的ViewModelStore activity.clearViewModelStore() }
内容的提问来源于stack exchange,提问作者Scrobot

