我的android项目中有两个模块,即app模块和lib模块。
这两个模块都需要Koin进行D.I.,因此我在app模块的startKoin
类中调用MyApplication
,在lib模块中调用IninKointContentProvider
,如下所示。
// app module
class MyApplication : Application() {
override fun onCreate() {
super.onCreate()
startKoin(this, modules1)
}
}
// lib module
class InitKoinContentProvider : ContentProvider() {
override fun onCreate(): Boolean {
startKoin(context.applicationContext, modules2)
return true
}
}
然后应用程序崩溃并显示此消息
Caused by: org.koin.error.BeanOverrideException: Try to override definition with Single [class='android.content.Context'], but override is not allowed. Use 'override' option in your definition or module.
我猜startKoin
只能被叫一次。
我发现的解决方案是合并两个koin模块,然后在startKoin
中调用MyApplication
,但我不喜欢它。库模块可以由不使用koin的其他android项目导入,在这种情况下,我认为在startKoin
中调用InitKoinContentProvider
更好。
任何解决此问题的方法??谢谢!
答案 0 :(得分:3)
TL; DR
在提供您的依赖项来覆盖之前加载的模块所提供的依赖项时,请使用single/factory
方法,并将override
参数设置为true
。
single<Manager>(override = true) { TestManager() }
当我出于UI测试目的试图覆盖其中一个依赖项时,遇到了类似的问题。
当我在Application.onCreate()
中设置时:
startKoin {
module {
single { Printer() }
}
}
,然后在before
测试方法中:
loadKoinModules(module {
single<Printer> { TestPrinter() }
})
我在测试过程中遇到了运行时异常:
org.koin.core.error.DefinitionOverrideException: Already existing definition or try to override an existing one
解决方案是通过使用override
函数的 single
参数,向Koin证明您有意有意覆盖了该依赖性:< / p>
loadKoinModules(module {
single<Printer>(override = true) { TestPrinter() }
})
答案 1 :(得分:2)
要在其他项目模块上初始化额外的koin模块,并且不会出现重复的加载问题(例如,按Home键,而不是返回到活动),请转到模块声明文件:
val myModule = module {
single { MyRepository(get()) }
viewModel { MyViewModel(get()) }
}
private val loadKoinModules by lazy {
loadKoinModules(myModule)
}
fun inject() = loadKoinModules
然后在您的视图上:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
inject()
}
答案 2 :(得分:0)
根据设计startKoin
可以从Application类调用。您可以在lib中提供是否调用startKoin
的参数。但是我怀疑在库中包含Koin之类的东西是否是一种好习惯。如果应用程序已经包含Koin,但版本不同,该怎么办?
答案 3 :(得分:0)
在库模块中,使用loadKoinModules()
加载特定于模块的koin模块。 Docs。
在此之前,您需要运行startKoin()
,因此与内容提供程序的初始化顺序可能会有些棘手。
答案 4 :(得分:0)
我发现了受到@laalto的回答启发的最佳解决方案,谢谢!
升级到koin 2.0,然后使用KoinApplication和自定义的KoinComponent创建一个独立的koin上下文,它可以让lib模块使用koin,而无需通过应用程序模块进行任何初始化调用,仍然可以在ContentProvider中启动koin。整个代码可能如下所示。
// app module
class MyApplication : Application() {
override fun onCreate() {
super.onCreate()
startKoin {
androidContext(this@MyApplication)
modules(module{
viewModel { MainViewModel() }
})
}
}
}
class MainActivity: AppCompactActivity() {
private val viewModel: MainViewModel by viewModel()
}
// lib module
internal object MyKoinContext {
lateinit var koinApplication: KoinApplication
}
interface MyKoinComponent : KoinComponent {
override fun getKoin(): Koin {
return MyKoinContext.koinApplication.koin
}
}
class InitKoinContentProvider : ContentProvider() {
override fun onCreate(): Boolean {
MyKoinContext.koinApplication = koinApplication {
androidContext(context.applicationContext)
modules(module{
viewModel { FooViewModel() }
})
}
return true
}
}
class FooActivity: AppCompactActivity(), MyKoinComponent {
private val viewModel: FooViewModel by viewModel()
}
参考: https://insert-koin.io/docs/2.0/documentation/reference/index.html#_koin_context_isolation