Как использовать Koin в нескольких модулях?

В моем проекте Android есть два модуля: модуль приложения и модуль библиотеки.

Оба этих двух модуля нуждаются в Koin для D.I., поэтому я вызываю startKoin в MyApplication классе в модуле приложения и IninKointContentProvider в модуле lib, как показано ниже.

// 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 можно вызвать только один раз.

Решение, которое я нашел, - это объединение двух модулей коинов с последующим вызовом startKoin в MyApplication, но мне это не нравится. Модуль lib может быть импортирован другим проектом Android, который не использует коин, в этом случае я думаю, что вызов startKoin в InitKoinContentProvider лучше.

Любое решение этой проблемы ?? Спасибо!


person aiueoH    schedule 24.05.2019    source источник


Ответы (5)


Я нашел лучшее решение, вдохновленное ответом @laalto, спасибо!

Обновитесь до koin 2.0, затем используйте KoinApplication и настроенный KoinComponent для создания изолированного контекста koin, он может позволить модулю lib использовать koin без какого-либо инициализирующего вызова модулем приложения, по-прежнему запускать koin в ContentProvider. Весь код может понравиться ниже.

// 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

person aiueoH    schedule 29.05.2019
comment
Сбой, вызванный: kotlin.UninitializedPropertyAccessException: свойство lateinit koinApplication не было инициализировано в koin 2.1.6 - person mrahimygk; 14.07.2020

Чтобы запустить дополнительные модули коинов в других модулях проекта и не получить дублирующихся проблем с загрузкой (например, нажатие кнопки домой, а не возвращение к активности), перейдите в файл объявления вашего модуля:

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()
}
person gusgol    schedule 25.09.2019

TL; DR Используйте single/factory методы с параметром override, установленным на true, при предоставлении ваших зависимостей, которые переопределяют те, которые предоставлены ранее загруженными модулями.

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

Я столкнулся с аналогичной проблемой, когда попытался переопределить одну из зависимостей для целей тестирования пользовательского интерфейса. Когда я настраиваю в 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, что вы намеренно отменяете эту зависимость, используя параметр override функции single следующим образом:

loadKoinModules(module {
    single<Printer>(override = true) { TestPrinter() }
})
person Andrew Panasiuk    schedule 08.01.2020

В модулях библиотеки используйте loadKoinModules() для загрузки модулей koin для конкретных модулей. Документы.

Перед этим вам необходимо запустить startKoin(), поэтому порядок инициализации с поставщиками контента может быть немного сложным.

person laalto    schedule 24.05.2019
comment
Например, мы перенаправляем пользователя в новый функциональный модуль. Можно ли вызвать loadKoinModules() в onCreate() первого Activitie в этом модуле? - person Levon Petrosyan; 18.06.2019
comment
Спасибо за ответ - person Levon Petrosyan; 18.06.2019

По замыслу startKoin должен вызываться из класса Application. Вы можете указать в библиотеке параметр, следует ли вызывать startKoin или нет. Но я сомневаюсь, что включение таких вещей, как Koin, в библиотеки - это хорошая практика. Что делать, если в приложении уже есть Koin, но другой версии?

person Anton Malyshev    schedule 24.05.2019