Koin је DI фрејмворк за Kotlin који ради без генерације кода, рефлексије и анотација. Библиотека користи DSL за описивање модула и убризгава зависности кроз лаки контејнер са подршком за Android, Ktor и Multiplatform. Према званичној документацији Koin, фрејмворк пружа модуле, скопове и уграђену подршку за Jetpack Compose са минималним boilerplate.
Главно
Koin — DI фрејмворк за Kotlin, написан у чистом језику без коришћења рефлексије, анотација или генерације кода. За разлику од Dagger Hilt, који захтева процесор анотација и генерацију кода у фази компилације, Koin ради искључиво у runtime-у користећи лаки 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 због генерисаног кода.
Контејнер Koin се иницијализује кроз функцију startKoin која прима ламбду са конфигурацијом. Унутар ове ламбде описују се модули са регистрацијама — основни градивни блок DI логике.
Функција startKoin креира глобални контејнер доступан из било које тачке апликације кроз GlobalContext, али у вишемодулним пројектима се препоручује коришћење KoinApplication за креирање изолованих контејнера. У Android-у за иницијализацију се користи AndroidContext који се аутоматски везује за животни циклус Application. Модули се региструју кроз параметар modules који прима листу инстанци Module.
val networkModule = module {
single {
OkHttpClient()
}
single {
Retrofit.Builder()
.baseUrl("https://api.example.com")
.build()
}
}
startKoin {
modules(networkModule)
}
Сваки модул садржи дефиниције кроз single (синглтон) или factory (нова инстанца). Дефиниције могу да се позивају на друге регистроване зависности кроз get(), што формира граф убризгавања без експлицитног наводљења типова и без boilerplate кода.
Koin активно користи inline функције са reified параметрима за извођење типа из контекста. То омогућава писање регистрација без експлицитног наводљења класе: single { MyService() } аутоматски одређује тип на основу повраћне вредности ламбде.
За разлику од Dagger-а, Koin не проверава граф зависности у фази компилације — све грешке се откривају у runtime-у при првом приступу нерешеној зависности. То је компромис који значајно поједностављује код и убрзава изградњу, али захтева тестно покривање DI конфигурације. Многе тимове бирају Koin управо због брзине развоја и једноставности, упркос непостојању провера у време компилације.
У верзији Koin 3.5 појавила се експериментална провера графа у фази компилације кроз плагин Koin Annotations. Програмер додаје анотације @Module и @KoinComponent, а плагин генерише код за валидацију који се покреће при изградњи. Међутим, главна предност Koin-а — одсуство генерације кода — се губи у овом режиму, па већина тимова и даље користи класични DSL приступ са runtime проверама кроз тестове.
Koin пружа неколико начина убризгавања зависности: by inject(), get() и директно прослеђивање кроз конструктор. Избор начина зависи од контекста коришћења.
Делегат by inject — најчешћи начин убризгавања у ViewModel и Android фрагментима. Зависност се иницијализује лењиво — само при првом приступу својству. То је ефикасно за ресурсно захтевне сервисе који можда неће бити потребни одмах.
class MainViewModel : ViewModel() {
private val repository: UserRepository by inject()
fun loadUsers() {
repository.fetchAll()
}
}
Функција get враћа инстанцу зависности одмах. Користи се унутар фабричких ламбди при регистрацији или када је зависност потребна у синхроном контексту без лењиве иницијализације. За разлику од by inject(), get() не подржава лењиво учитавање и захтева да контејнер већ буде иницијализован у тренутку позива.
Scope у Koin-у — механизам везивања животног века зависности за одређену компоненту, као што су Activity, Fragment или корисничка сесија. То је кључна функционалност за управљање меморијом у Android апликацијама.
Функција scope унутар модула креира скоп који живи докле је жив повезани компонент. Све зависности регистроване у скопу се уништавају при његовом затварању, што спречава цурење меморије.
val userScope = module {
scope<UserSession> {
scoped {
UserRepository(get())
}
scoped {
SessionManager(get())
}
}
}
Функција scoped региструје зависност која ће постојати само унутар скопа. При затварању скопа, сви scoped објекти постају недоступни за сакупљача отпада.
single — регистрација једне инстанце за целу апликацију са лењивом иницијализацијом. Користи се за сервисе без стања: мрежне клијенте, кешеве, логере.
factory — сваки позив get() креира нову инстанцу. Примењује се за ViewModel, репозиторијуме и објекте са стањем где је важна свежа инстанца при сваком приступу.
Интеграција Koin-а у Android пројекат је минимална: довољно је додати зависност у build.gradle и позвати startKoin у Application.onCreate. Koin пружа модуле за интеграцију са Jetpack Compose, Navigation и WorkManager, што га чини потпуном алтернативом Hilt-у.
Посебна библиотека koin-android-compose омогућава убризгавање зависности директно у Composable функцијама кроз функције koinViewModel() и koinInject(). То елиминише потребу за прослеђивањем контејнера кроз параметре сваког екрана и чини ViewModel код чишчијим због аутоматског повезивања са животним циклусом.
Према Google I/O 2024, Jetpack Compose је постао главни фрејмворк за нове Android пројекте. Koin пружа нативну подршку Compose-а без додатног подешавања, аутоматски везујући скопове са животним циклусом ViewModel-а кроз koinViewModel() узимајући у обзир coroutine контекст.
За тестирање, Koin пружа функције koinTest и koinTestRule које стварају изоловани тестни контејнер са тестним модулима и аутоматски га затварају након теста. То осигурава изолацију тестова и спречава цурење стања између тест случајева.
Интеграција Koin-а са Jetpack Navigation се остварује кроз модул koin-androidx-navigation. ViewModel сваког екрана аутоматски добија зависности кроз by viewModel() са прослеђивањем SavedStateHandle за очување стања при ротацији екрана и обнављање након минимизације апликације.
За јединично тестирање ViewModel-а са Koin-ом користи се koinTestRule из библиотеке koin-test-junit5 или koin-test-junit4. Правило ствара изоловани контејнер са тестним модулима пре сваког теста и га аутоматски затвара након завршетка, спречавајући цурење стања између тест случајева. Стварне зависности се замењују моковима кроз MockK: модул са регистрацијама single
Једна од кључних могућности Koin 3.x је подршка за Ktor за креирање серверских апликација у Kotlin-у и Compose Multiplatform за десктоп апликације. То чини Koin јединим DI фрејмворком који покрива све три Kotlin платформе без промене парадигме убризгавања. Модул koin-ktor омогућава регистрацију зависности кроз install(Koin) у Application блоку и убризгавање сервиса у руте кроз by inject() исто као у Android-у. То чини Koin универзалним DI решењем за Kotlin пројекте било које архитектуре — од мобилног клијента до серверског бекенда.
Интеграција Koin-а са Jetpack Navigation кроз модул koin-androidx-navigation елиминише потребу за ручним креирањем ViewModelProvider.Factory за сваки екран. За вишемодулне пројекте, Koin подржава лењиво учитавање модула кроз loadKoinModules, што омогућава независно повезивање DI конфигурације сваког feature модула.
Често постављана питања
Koin ради у runtime-у без генерације кода и анотација, што убрзава изградњу, али не проверава граф зависности у фази компилације. Hilt генерише код при компилацији и открива грешке DI раније, али захтева сложену конфигурацију и успорава изградњу.
Да, Koin потпуно подржава Kotlin Multiplatform. Библиотека koin-core ради на свим Kotlin платформама, а koin-android и koin-compose додају могућности специфичне за платформу за Android и iOS.
Цикличне зависности доводе до StackOverflowError-а у runtime-у. Koin их не открива аутоматски. Решење — рефакторисање архитектуре: издвајање заједничког интерфејса, коришћење обрасца Listener/Observer или прекидање циклуса кроз фабрику са одложеном иницијализацијом.
У Android-у, скопови се могу повезати са животним циклусом Activity или Fragment-а кроз AndroidScope. При уништавању компоненте, Koin аутоматски затвара одговарајући скоп. У прилагођеним скоповима (корисничка сесија), затварање се обавља ручно позивом scope.close.
Користите функцију koinTest из модула koin-test. Она ствара изоловани контејнер са тестним модулима који се аутоматски затвара након теста. Стварне зависности се замењују моковима кроз модул са Mockito или MockK.
Закључак
Развићемо мобилну апликацију под кључ
IT Sectr креира iOS и Android апликације за стартапе и предузећа од 2017. године. Саветоваћемо вас и предложити најбоље решење.
Прочитајте такође