如何在多个模块中使用Koin?

时间:2019-05-24 09:42:01

标签: android kotlin koin

我的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更好。

任何解决此问题的方法??谢谢!

5 个答案:

答案 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