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

wnvonmuf  于 2023-01-24  发布在  Android
关注(0)|答案(6)|浏览(335)

我的android项目中有两个模块,app模块和lib模块。
这两个模块都需要Koin进行D.I.,因此我在app模块的MyApplication类中调用startKoin,在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模块,然后在MyApplication中调用startKoin,但我不喜欢这样。库模块可能被其他不使用koin的android项目导入,在这种情况下,我认为在InitKoinContentProvider中调用startKoin更好。
有解决这个问题的方法吗??谢谢!

oyjwcjzk

oyjwcjzk1#

@laalto的回答启发我找到了最好的解决方案,谢谢!
升级到koin2.0,然后使用KoinApplication和自定义的KoinComponent创建一个隔离的koin上下文,它可以让lib模块使用koin,而不需要app模块进行任何初始化调用,仍然在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

2j4z5cfb

2j4z5cfb2#

在库模块中,使用loadKoinModules()加载特定于模块的koin modules.docs。
您需要在此之前运行startKoin(),因此内容提供程序的init顺序可能有点棘手。

yyyllmsg

yyyllmsg3#

要在其他项目模块上初始化额外的koin模块,并且避免重复加载问题(例如,按home键,然后返回Activity),请转到您的模块声明文件:

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()
}
w3nuxt5m

w3nuxt5m4#

TL;DR在提供覆盖之前加载的模块所提供的依赖项时,请使用override参数设置为truesingle/factory方法。

single<Manager>(override = true) { TestManager() }

当我为了UI测试而试图覆盖其中一个依赖项时,我遇到了类似的问题。

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
解决方案是向Koin表明您正在故意使用single函数的**override**param来覆盖该依赖项,如下所示:

loadKoinModules(module {
    single<Printer>(override = true) { TestPrinter() }
})
2q5ifsrm

2q5ifsrm5#

按照设计,startKoin是从Application类调用的。你可以在库中提供一个参数来决定是否调用startKoin。但是我怀疑在库中包含Koin这样的东西是否是一个好的实践。如果一个应用程序已经包含Koin,但是版本不同呢?

aor9mmx1

aor9mmx16#

这种方式对我来说非常有效:

@KoinExperimentalAPI 
class MainApplication : Application() {

    override fun onCreate() {
        super.onCreate()
        initKoin()
    }

    private fun initKoin() {
        startKoin {
            androidLogger()
            androidContext(this@MainApplication)
        }
        loadKoinModules(
            myFeatureModule
        )
    }
}

并像往常一样在特性中定义模块:

val myFeatureModule = module {
    factory<...> { ...() }

    single { ...() }

    viewModel { ...(get() }
}

相关问题