- Регистрация
- 23 Август 2023
- Сообщения
- 2 962
- Лучшие ответы
- 0
- Реакции
- 0
- Баллы
- 51
Offline
Введение
В Android-проектах Koin остаётся одной из самых популярных DI-библиотек, особенно в MVP/MVI и Compose-приложениях. Она проста в настройке, гибкая и хорошо работает в больших кодовых базах.
Но многие команды, стремясь к модульности и тестопригодности, начинают активно использовать функции loadKoinModules() и unloadKoinModules(). И здесь разработчики часто натыкаются на странную ошибку:
org.koin.core.error.NoBeanDefFoundException: No definition found for …
В этой статье я разберу:
Сценарий
Допустим, у нас проект со множеством feature-модулей. Вы хотите подгружать зависимости только тогда, когда модуль реально нужен, например, в on-demand фичах или UI-тестах. Вы делаете что-то вроде:
На первый взгляд – всё ок. Но при быстром переключении между экранами, в UI-тестах или при работе background-сервисов неожиданно вылезает NoBeanDefFoundException.
Почему так происходит
Если Activity пересоздаётся при повороте экрана или пользователь быстро переключается, ViewModel может ещё жить, а модуль уже выгружен → крэш.
Как стоит делать
1. Инициализируйте модули один раз
Загрузите все модули при старте приложения или используйте DI-компоненты в стиле Hilt/Dagger (для тех же целей). Не выгружайте их динамически, если нет крайней необходимости.
2. Если нужна условная регистрация – используйте флаги, а не выгрузку
Вместо удаления модуля используйте stub-имплементации или условные фабрики:
3. Для UI-тестов — отдельный KoinApp
Вместо loadKoinModules/unloadKoinModules проще запустить Koin заново с тестовыми модулями:
Рекомендация
loadKoinModules и особенно unloadKoinModules — это «escape hatch», а не штатный инструмент. Они могут быть полезны для плагинов или SDK, но не для обычных экранов и фичей. В большинстве production-сценариев надёжнее:
Koin — отличный инструмент, но важно помнить, что его контейнер глобальный. Динамическая загрузка и выгрузка модулей приводит к состояниям гонки и definition not found ошибкам.
Поэтому подход — минимизировать динамическое управление модулями и проектировать зависимости так, чтобы их можно было безопасно инжектить в любом месте приложения.
В Android-проектах Koin остаётся одной из самых популярных DI-библиотек, особенно в MVP/MVI и Compose-приложениях. Она проста в настройке, гибкая и хорошо работает в больших кодовых базах.
Но многие команды, стремясь к модульности и тестопригодности, начинают активно использовать функции loadKoinModules() и unloadKoinModules(). И здесь разработчики часто натыкаются на странную ошибку:
org.koin.core.error.NoBeanDefFoundException: No definition found for …
В этой статье я разберу:
что происходит под капотом при loadKoinModules/unloadKoinModules;
почему возникает ошибка definition not found;
как правильно организовать модули Koin без динамической загрузки.
Сценарий
Допустим, у нас проект со множеством feature-модулей. Вы хотите подгружать зависимости только тогда, когда модуль реально нужен, например, в on-demand фичах или UI-тестах. Вы делаете что-то вроде:

На первый взгляд – всё ок. Но при быстром переключении между экранами, в UI-тестах или при работе background-сервисов неожиданно вылезает NoBeanDefFoundException.
Почему так происходит
Koin работает с глобальным graph’ом.
loadKoinModules() добавляет определения в уже работающий контейнер. unloadKoinModules() их удаляет. Если параллельно что-то пытается получить зависимость — оно упадёт.
Асинхронность Android.
Даже если вы думаете, что «экран закрылся – зависимости больше не нужны», на деле корутины, WorkManager или callback-и могут продолжать жить и запрашивать зависимости.
ViewModelStore и жизненный цикл.
ViewModel может пережить activity или fragment (особенно при смене конфигурации). Если в этот момент вы выгрузите модуль, то при следующем get() он просто не найдёт определение.

Если Activity пересоздаётся при повороте экрана или пользователь быстро переключается, ViewModel может ещё жить, а модуль уже выгружен → крэш.
Как стоит делать
1. Инициализируйте модули один раз
Загрузите все модули при старте приложения или используйте DI-компоненты в стиле Hilt/Dagger (для тех же целей). Не выгружайте их динамически, если нет крайней необходимости.

2. Если нужна условная регистрация – используйте флаги, а не выгрузку
Вместо удаления модуля используйте stub-имплементации или условные фабрики:

3. Для UI-тестов — отдельный KoinApp
Вместо loadKoinModules/unloadKoinModules проще запустить Koin заново с тестовыми модулями:

Рекомендация
loadKoinModules и особенно unloadKoinModules — это «escape hatch», а не штатный инструмент. Они могут быть полезны для плагинов или SDK, но не для обычных экранов и фичей. В большинстве production-сценариев надёжнее:
загружать модули один раз при старте приложения,
использовать условные реализации,
или перезапускать Koin целиком в тестах.
Koin — отличный инструмент, но важно помнить, что его контейнер глобальный. Динамическая загрузка и выгрузка модулей приводит к состояниям гонки и definition not found ошибкам.
Поэтому подход — минимизировать динамическое управление модулями и проектировать зависимости так, чтобы их можно было безопасно инжектить в любом месте приложения.