Koin
Koin - фреймворк внедрения зависимостей, разработанный для Kotlin. Его классический API описывает модули и создание объектов через Kotlin DSL, без обязательных аннотаций и сгенерированных компонентов. Koin поддерживает Android, Kotlin Multiplatform и другие Kotlin-платформы.
Koin легко добавить в проект, но это всё равно контейнер, для которого нужно осознанно определить владельца, время жизни объектов и конфигурацию запуска.
Основы Koin
Модуль объединяет определения зависимостей. Если граф остаётся читаемым, лучше использовать ссылки на конструкторы:
val appModule = module {
singleOf(::DefaultUserRepository) bind UserRepository::class
factoryOf(::LoadUserUseCase)
viewModelOf(::UserViewModel)
}
Основные типы определений:
| Определение | Поведение | Типичное применение |
|---|---|---|
single |
Один экземпляр на контейнер Koin | База данных, API client, repository |
factory |
Новый экземпляр при каждом разрешении | Лёгкие независимые объекты |
scoped |
Один экземпляр внутри явного scope | Объекты с временем жизни сессии или экрана |
viewModel |
Создание через Android ViewModelProvider |
Android ViewModels |
single не означает глобальный singleton в JVM. Экземпляр принадлежит Koin application, содержащему определение, и по умолчанию создаётся лениво.
Koin запускают один раз, обычно из Application:
class App : Application() {
override fun onCreate() {
super.onCreate()
startKoin {
androidContext(this@App)
modules(appModule)
}
}
}
Обычные классы лучше не связывать с Koin и передавать им зависимости через конструктор. Разрешайте зависимости в composition roots, например в точках входа Android, а не вызывайте get() по всему бизнес-коду. Так зависимости остаются явными, а unit-тесты - простыми.
Qualifiers
Qualifier нужен, когда в графе есть несколько определений одного типа:
val networkModule = module {
single(named("authenticated")) { authenticatedClient(get()) }
single(named("public")) { publicClient() }
}
Строковые qualifiers лаконичны, но в них легко допустить опечатку. В большом графе их стоит централизовать или заменить типизированными qualifiers. Значения, известные только в месте вызова, можно передать через parametersOf(...); этот механизм стоит оставлять для настоящих runtime-данных, а не скрывать в нём долгоживущую конфигурацию.
Проверка и тестирование
В классическом DSL отсутствующие definitions, неверные qualifiers и некоторые циклы могут обнаружиться только при разрешении конкретной зависимости. Проверяйте модули в тестах и создавайте важные точки входа, а не ограничивайтесь успешным запуском приложения.
Современный Koin также предлагает annotations и DSL на основе compiler plugin, которые могут генерировать wiring и находить больше ошибок во время сборки. Они повышают безопасность, но являются отдельными возможностями и не меняют поведение классического runtime DSL, если проект их не подключил.
В тестах можно использовать тестовые модули, однако для unit-тестов часто проще напрямую вызвать конструктор. Между тестами Koin application нужно изолировать, чтобы избежать общего состояния и зависимости от порядка запуска.
Koin и Hilt
| Тема | Hilt | Классический DSL Koin |
|---|---|---|
| Построение графа | Сгенерированные компоненты Dagger | Runtime-контейнер |
| Обнаружение ошибок | Строгая compile-time проверка | В основном при разрешении; помогают проверки модулей |
| Конфигурация | Аннотации, модули и готовые компоненты | Definitions через Kotlin DSL |
| Время жизни Android-объектов | Стандартная иерархия сгенерированных компонентов | Интеграция с ViewModel и явные scopes |
| Multiplatform | Граф Dagger/Hilt ориентирован на Android/JVM | Koin Core поддерживает Kotlin Multiplatform |
Hilt - сильный вариант по умолчанию, если большому Android-графу нужны строгая compile-time проверка и стандартные lifecycle components. Koin привлекателен, когда команде важны лаконичная Kotlin-конфигурация, быстрое внедрение или общая Kotlin Multiplatform инфраструктура.
Не стоит выбирать фреймворк только по размеру проекта. Учитывайте опыт команды, build tooling, момент обнаружения ошибок, владение lifecycle, стратегию тестирования и границы платформ в графе зависимостей.