为何Koin Factory生成的AdRepository参数被缓存?如何重新调用?
问题描述
我在Koin模块中定义了需要参数的AdRepository:
factory<AdRepository> { params -> DefaultAdRepository( inputAdId = params.get(), api = get(), log = get(), mapper = get() ) }
接着在ViewModel中通过parametersOf()获取该Repository:
viewModel { params -> val adId: Long = params.get() AdvertisementDetailViewModel( inputAdId = adId, adRepository = get(parameters = { parametersOf(adId) }), log = get() ) }
但实际运行时出现异常:每次进入/离开页面导致ViewModel重建时,adRepository的参数并未更新——Repository的init块仅在第一次实例化ViewModel时执行,后续即使ViewModel参数正确,Repository始终保留第一次的参数值。手动构造Repository可解决问题,但希望通过Koin原生功能实现带参数ViewModel对应带参数Repository的注入。
问题原因
Koin在ViewModel的专属作用域(ViewModelScope)内,默认会缓存通过get()获取的factory类型依赖实例。尽管factory本身设计为每次调用生成新实例,但当Koin判断当前作用域内已存在同类型依赖时,会忽略参数变化直接复用旧实例。当ViewModel因页面进出重建时,若Koin未识别到作用域的变更,就会导致Repository复用旧参数的实例。
解决方案
1. 明确参数类型,避免解析错误
在Repository的factory定义中明确指定参数类型,确保Koin正确解析参数:
factory<AdRepository> { params -> DefaultAdRepository( inputAdId = params.get<Long>(), // 显式指定参数类型 api = get(), log = get(), mapper = get() ) }
2. 为Repository添加唯一标识
通过named限定符结合参数生成唯一标识,让Koin区分不同参数的Repository实例:
// 定义带限定符的factory factory<AdRepository>(qualifier = named { params -> "ad_repo_${params.get<Long>()}" }) { params -> DefaultAdRepository( inputAdId = params.get<Long>(), api = get(), log = get(), mapper = get() ) } // ViewModel中获取对应标识的Repository viewModel { params -> val adId: Long = params.get() AdvertisementDetailViewModel( inputAdId = adId, adRepository = get(named("ad_repo_$adId"), parameters = { parametersOf(adId) }), log = get() ) }
3. 将Repository改为scoped绑定
由于每个ViewModel对应独立的ViewModelScope,将Repository改为scoped绑定后,每个ViewModel作用域内会生成唯一的Repository实例,ViewModel重建时会自动创建新的Repository:
// 改为scoped绑定 scoped<AdRepository> { params -> DefaultAdRepository( inputAdId = params.get<Long>(), api = get(), log = get(), mapper = get() ) } // ViewModel中获取方式不变 viewModel { params -> val adId: Long = params.get() AdvertisementDetailViewModel( inputAdId = adId, adRepository = get(parameters = { parametersOf(adId) }), log = get() ) }
4. 强制获取新实例
使用Koin的getNew()方法替代get(),强制每次调用factory生成新实例:
viewModel { params -> val adId: Long = params.get() AdvertisementDetailViewModel( inputAdId = adId, adRepository = getNew(parameters = { parametersOf(adId) }), log = get() ) }
内容的提问来源于stack exchange,提问作者Grzegorz Adam Hankiewicz

