Koin — какво е, принципи на Dependency Injection и как работи

Автор: IT Sectr Публикувано: 2026-05-04 Време за четене: 8 мин

Koin е DI рамка за Kotlin, която работи без генериране на код, рефлексия и анотации. Библиотеката използва DSL за описване на модули и инжектира зависимости чрез лек контейнер с поддръжка на Android, Ktor и Multiplatform. Според официалната документация на Koin, рамката предоставя модули, скоупове и вградена поддръжка за Jetpack Compose с минимален boilerplate.

Основни точки

  • Koin — DI рамка за Kotlin, използваща DSL без рефлексия и генериране на код.
  • Модул — логическо групиране на регистрации на зависимости чрез функциите single и factory.
  • single — регистрация на сингълтън, чийто екземпляр се създава веднъж.
  • factory — регистрация, при която всяка заявка създава нов екземпляр.
  • Scope — механизъм за свързване на жизнения цикъл на зависимост с компонент, например с Activity.

Какво е Koin и с какво се различава

Koin — DI рамка за Kotlin, написана на чист език без използване на рефлексия, анотации или генериране на код. За разлика от Dagger Hilt, който изисква процесор за анотации и генериране на код по време на компилация, Koin работи изключително по време на изпълнение, използвайки лек DSL за описване на модули.

Основната идея на Koin е да предостави прост API за регистриране и разрешаване на зависимости без необходимост от изучаване на сложни концепции за графи на зависимости и дървета на компоненти. Разработчикът описва кои класове са достъпни за контейнера, а Koin ги инжектира автоматично чрез конструктора или мързеливи делегати by inject. Рамката е напълно съвместима с Kotlin Multiplatform, позволявайки единен DI подход на Android, iOS и сървърната страна.

Според проучване на Kotlin Developers Community (2025), Koin се използва в 31% от търговските Android проекти, като по популярност отстъпва само на Hilt (47%). Основната причина за избора е простотата на конфигурация и липсата на необходимост от генериране на код, което ускорява изграждането на проекта.

Изберете Koin за средни и големи проекти, където бързото стартиране на разработката е важно, или за Kotlin Multiplatform решения, където Hilt не е наличен по архитектурни причини.

Koin не използва рефлексия и генериране на код — всички регистрации се основават на inline функции с reified типове, които по време на компилация заместват конкретния тип в тялото на функцията. Това прави Koin една от най-леките DI рамки по отношение на размера на крайния APK: добавянето на Koin увеличава размера на приложението само със 100–150 KB, докато Dagger Hilt добавя около 500 KB поради генерирания код.

Как работят контейнерът и DSL на Koin

Контейнерът Koin се инициализира чрез функцията startKoin, която приема ламбда с конфигурация. Вътре в тази ламбда се описват модули с регистрации — основният градивен елемент на DI логиката.

startKoin и модули

Функцията startKoin създава глобален контейнер, достъпен от всяка точка на приложението чрез GlobalContext, но в мултимодулни проекти се препоръчва използването на KoinApplication за създаване на изолирани контейнери. В Android за инициализация се използва AndroidContext, който автоматично се свързва с жизнения цикъл на Application. Модулите се регистрират чрез параметъра modules, който приема списък от инстанции на Module.

kotlin
val networkModule = module {
    single {
        OkHttpClient()
    }
    single {
        Retrofit.Builder()
            .baseUrl("https://api.example.com")
            .build()
    }
}

startKoin {
    modules(networkModule)
}

Всеки модул съдържа дефиниции чрез single (сингълтън) или factory (нов екземпляр). Дефинициите могат да се позовават на други регистрирани зависимости чрез get(), формирайки граф на инжектиране без изрично посочване на типове и без boilerplate код.

DSL и inline функции

Koin активно използва inline функции с reified параметри за извеждане на тип от контекста. Това позволява писане на регистрации без изрично посочване на клас: single { MyService() } автоматично определя типа въз основа на върнатата стойност на ламбдата.

За разлика от Dagger, Koin не проверява графа на зависимости по време на компилация — всички грешки се откриват по време на изпълнение при първия достъп до неразрешена зависимост. Това е компромис, който значително опростява кода и ускорява изграждането, но изисква тестово покритие на DI конфигурацията.

Във версия Koin 3.5 се появи експериментална проверка на графа по време на компилация чрез плъгина Koin Annotations. Основното предимство на Koin — липсата на генериране на код — обаче се губи в този режим, затова повечето екипи продължават да използват класическия DSL подход с проверки по време на изпълнение чрез тестове.

Начини за инжектиране на зависимости в Koin

Koin предоставя няколко начина за инжектиране на зависимости: by inject(), get() и директно предаване чрез конструктор. Изборът на начин зависи от контекста на използване.

by inject() — мързеливо инжектиране

Делегатът by inject — най-честият начин за инжектиране в ViewModel и Android фрагменти. Зависимостта се инициализира мързеливо — само при първия достъп до свойството.

kotlin
class MainViewModel : ViewModel() {
    private val repository: UserRepository by inject()

    fun loadUsers() {
        repository.fetchAll()
    }
}

get() — изрично получаване

Функцията get връща екземпляра на зависимостта незабавно. За разлика от by inject(), get() не поддържа мързеливо зареждане и изисква контейнерът вече да е инициализиран към момента на извикването.

Модули и скоупове в Koin

Scope в Koin — механизъм за свързване на жизнения цикъл на зависимостите с конкретен компонент, като Activity, Fragment или потребителска сесия.

scope — свързване с компонент

Функцията scope вътре в модул създава скоуп, който живее докато живее свързаният компонент. Всички зависимости, регистрирани в скоупа, се унищожават при затварянето му, предотвратявайки изтичане на памет.

kotlin
val userScope = module {
    scope<UserSession> {
        scoped {
            UserRepository(get())
        }
        scoped {
            SessionManager(get())
        }
    }
}

Функцията scoped регистрира зависимост, която ще съществува само вътре в скоупа.

single vs factory

single — регистрация на един екземпляр за цялото приложение с мързелива инициализация. factory — всяко извикване на get() създава нов екземпляр, използва се за ViewModel, хранилища и обекти със състояние.

Koin в Android проекти

Интеграцията на Koin в Android проект е минимална: просто добавете зависимостта в build.gradle и извикайте startKoin в Application.onCreate. Koin предоставя модули за интеграция с Jetpack Compose, Navigation и WorkManager.

Специалната библиотека koin-android-compose позволява директно инжектиране на зависимости в Composable функции чрез koinViewModel() и koinInject().

Според Google I/O 2024, Jetpack Compose се превърна в основната рамка за нови Android проекти. Koin предоставя естествена поддръжка на Compose без допълнителна конфигурация.

За тестване Koin предоставя функциите koinTest и koinTestRule, които създават изолиран тестов контейнер с тестови модули. Интеграцията с Jetpack Navigation се осъществява чрез модула koin-androidx-navigation.

За единично тестване на ViewModel с Koin се използва koinTestRule от библиотеката koin-test-junit5 или koin-test-junit4. Реалните зависимости се заменят с мокове чрез MockK.

Една от ключовите възможности на Koin 3.x е поддръжката на Ktor за създаване на сървърни приложения в Kotlin и Compose Multiplatform за десктоп приложения. Това прави Koin единствената DI рамка, покриваща и трите Kotlin платформи без промяна на парадигмата на инжектиране.

За мултимодулни проекти Koin поддържа мързеливо зареждане на модули чрез loadKoinModules, което позволява независимо свързване на DI конфигурацията на всеки feature модул.

Често задавани въпроси

С какво Koin се различава от Dagger Hilt?

Koin работи по време на изпълнение без генериране на код и анотации, което ускорява изграждането, но не проверява графа на зависимости по време на компилация. Hilt генерира код при компилация и открива DI грешки по-рано, но изисква сложна конфигурация и забавя изграждането.

Поддържа ли Koin Kotlin Multiplatform?

Да, Koin напълно поддържа Kotlin Multiplatform. Библиотеката koin-core работи на всички Kotlin платформи.

Как да се справим с циклични зависимости в Koin?

Цикличните зависимости водят до StackOverflowError по време на изпълнение. Koin не ги открива автоматично. Решението е рефакториране на архитектурата.

Трябва ли ръчно да затваряме скоуповете?

В Android скоуповете могат да бъдат свързани с жизнения цикъл на Activity или Fragment чрез AndroidScope. При унищожаване на компонента Koin автоматично затваря съответния скоуп.

Как да тестваме код с Koin?

Използвайте функцията koinTest от модула koin-test. Тя създава изолиран контейнер с тестови модули, който се затваря автоматично след теста.

Обобщение

  • Koin — DI рамка за Kotlin с DSL без рефлексия, анотации и генериране на код.
  • Модулите групират регистрации чрез single (сингълтън) и factory (нов екземпляр).
  • startKoin инициализира глобалния контейнер, достъпен чрез by inject() и get().
  • Scope свързва жизнения цикъл на зависимостите с жизнения цикъл на Android компонент.
  • Инжектирането в Compose се осъществява чрез koinViewModel() и koinInject().
  • Koin поддържа Kotlin Multiplatform за единен DI подход на всички платформи.
  • DI грешките се откриват по време на изпълнение, затова тестовото покритие на конфигурацията е задължително.

Ще разработим мобилно приложение под ключ

IT Sectr създава iOS и Android приложения за стартъпи и бизнеси от 2017 г. Ще ви консултираме и ще предложим най-доброто решение.

Обсъдете проекта

Прочетете също