【问题标题】:How to use Koin in multiple module?如何在多个模块中使用 Koin?
【发布时间】:2019-05-24 09:42:00
【问题描述】:

我的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,但我不喜欢它。 Lib模块可能被其他不使用koin的android项目导入,在这种情况下,我认为在InitKoinContentProvider中调用startKoin更好。

这个问题有什么解决办法吗??谢谢!

【问题讨论】:

    标签: android kotlin koin


    【解决方案1】:

    我找到了受@laalto 回答启发的最佳解决方案,谢谢!

    升级到 koin 2.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

    【讨论】:

    • 崩溃原因:kotlin.UninitializedPropertyAccessException: lateinit property koinApplication has not been initialized in koin 2.1.6
    【解决方案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()
    }
    

    【讨论】:

      【解决方案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

      解决方案是向 Koin 表明您 有意 通过使用 single 函数的 override 参数来覆盖该依赖项:

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

      【讨论】:

        【解决方案4】:

        在您的库模块中,使用 loadKoinModules() 加载特定于模块的 koin 模块。 Docs.

        您需要在此之前运行startKoin(),因此内容提供者的初始化顺序可能有点棘手。

        【讨论】:

        • 例如我们将用户重定向到一个新的功能模块,在该模块上的第一个 Activity 的onCreate() 中调用loadKoinModules() 可以吗?
        • 感谢回复
        【解决方案5】:

        根据设计,startKoin 旨在从 Application 类中调用。您可以在库中提供一个参数是否调用startKoin。但我怀疑在库中包含诸如 Koin 之类的东西是一种好习惯。如果应用程序已经包含 Koin,但版本不同怎么办?

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2020-07-03
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2018-11-15
          • 1970-01-01
          • 1970-01-01
          • 2020-11-17
          相关资源
          最近更新 更多