Koin este un framework DI pentru Kotlin care funcționează fără generare de cod, reflecție și adnotări. Biblioteca folosește DSL pentru a descrie module și injectează dependențe printr-un container ușor cu suport pentru Android, Ktor și Multiplatform. Conform documentației oficiale Koin, framework-ul oferă module, scopuri și suport integrat pentru Jetpack Compose cu boilerplate minim.
Principalele puncte
Koin — framework DI pentru Kotlin, scris în limbaj pur fără a utiliza reflecție, adnotări sau generare de cod. Spre deosebire de Dagger Hilt, care necesită un procesor de adnotări și generare de cod la compilare, Koin funcționează exclusiv în runtime folosind un DSL ușor pentru descrierea modulelor.
Ideea principală a Koin este de a oferi o API simplă pentru înregistrarea și rezolvarea dependențelor fără a fi nevoie să înveței concepte complexe de grafuri de dependență și arbori de componente. Dezvoltatorul descrie ce clase sunt disponibile containerului, iar Koin le injectează automat prin constructor sau delegați lenji by inject. Framework-ul este complet compatibil cu Kotlin Multiplatform, permițând utilizarea unei abordări unitare DI pe Android, iOS și partea de server.
Conform sondajului Kotlin Developers Community (2025), Koin este utilizat în 31% din proiectele Android comerciale, fiind depășit ca popularitate doar de Hilt (47%). Motivul principal al alegerii — simplitatea configurării și absența necesității de generare a codului, ceea ce accelerează construirea proiectului.
Alegeți Koin pentru proiecte medii și mari unde este important un start rapid al dezvoltării sau pentru soluții Kotlin Multiplatform unde Hilt este indisponibil din motive arhitecturale.
Koin nu utilizează reflecție și generare de cod — toate înregistrările se bazează pe funcții inline cu tipuri reified care la compilare substituie tipul concret în corpul funcției. Acest lucru face din Koin unul dintre cele mai ușoare framework-uri DI ca dimensiune a APK-ului final: adăugarea Koin crește dimensiunea aplicației cu doar 100–150 KB, în timp ce Dagger Hilt adaugă aproximativ 500 KB din cauza codului generat.
Containerul Koin este inițializat prin funcția startKoin care primește o lambda cu configurație. În interiorul acestei lambda sunt descrise modulele cu înregistrări — blocul principal de construcție al logicii DI.
Funcția startKoin creează un container global accesibil din orice punct al aplicației prin GlobalContext, dar în proiecte multi-modul se recomandă utilizarea KoinApplication pentru crearea de containere izolate. În Android pentru inițializare se folosește AndroidContext care se leagă automat de ciclul de viață al Application. Modulele sunt înregistrate prin parametrul modules care primește o listă de instanțe Module.
val networkModule = module {
single {
OkHttpClient()
}
single {
Retrofit.Builder()
.baseUrl("https://api.example.com")
.build()
}
}
startKoin {
modules(networkModule)
}
Fiecare modul conține definiții prin single (singleton) sau factory (instanță nouă). Definițiile pot face referire la alte dependențe înregistrate prin get(), formând un graf de injectare fără specificarea explicită a tipurilor și fără cod boilerplate.
Koin utilizează activ funcții inline cu parametri reified pentru deducerea tipului din context. Aceasta permite scrierea înregistrărilor fără specificarea explicită a clasei: single { MyService() } determină automat tipul pe baza valorii returnate de lambda.
Spre deosebire de Dagger, Koin nu verifică graful de dependențe la compilare — toate erorile sunt detectate în runtime la prima accesare a unei dependențe nerezolvate. Acesta este un compromis care simplifică semnificativ codul și accelerează construirea, dar necesită acoperire cu teste a configurației DI. Multe echipe aleg Koin tocmai pentru viteza de dezvoltare și simplitate, în ciuda absenței verificărilor la compilare.
În versiunea Koin 3.5 a apărut verificarea experimentală a grafului la compilare prin pluginul Koin Annotations. Dezvoltatorul adaugă adnotările @Module și @KoinComponent, iar pluginul generează cod de validare care rulează la construire. Cu toate acestea, principalul avantaj al Koin — absența generării de cod — se pierde în acest mod, motiv pentru care majoritatea echipelor continuă să utilizeze abordarea clasică DSL cu verificări în runtime prin teste.
Koin oferă mai multe metode de injectare a dependențelor: by inject(), get() și transmiterea directă prin constructor. Alegerea metodei depinde de contextul de utilizare.
Delegatul by inject — cea mai comună metodă de injectare în ViewModel și fragmente Android. Dependența este inițializată lent — doar la prima accesare a proprietății. Aceasta este eficientă pentru servicii consumatoare de resurse care ar putea să nu fie necesare imediat.
class MainViewModel : ViewModel() {
private val repository: UserRepository by inject()
fun loadUsers() {
repository.fetchAll()
}
}
Funcția get returnează instanța dependenței imediat. Este utilizată în interiorul lambda-urilor de fabrică la înregistrare sau când dependența este necesară într-un context sincron fără inițializare lentă. Spre deosebire de by inject(), get() nu suportă încărcarea lentă și necesită ca containerul să fie deja inițializat la momentul apelării.
Scope în Koin — mecanism de asociere a duratei de viață a dependențelor de un component specific, precum Activity, Fragment sau sesiune de utilizator. Aceasta este o funcționalitate cheie pentru gestionarea memoriei în aplicațiile Android.
Funcția scope în interiorul modulului creează un scop care trăiește cât timp trăiește componentul asociat. Toate dependențele înregistrate în scop sunt distruse la închiderea acestuia, prevenind scurgerile de memorie.
val userScope = module {
scope<UserSession> {
scoped {
UserRepository(get())
}
scoped {
SessionManager(get())
}
}
}
Funcția scoped înregistrează o dependență care va exista doar în interiorul scopului. La închiderea scopului, toate obiectele scoped devin inaccesibile pentru colectorul de gunoi.
single — înregistrarea unei singure instanțe pentru întreaga aplicație cu inițializare lentă. Utilizat pentru servicii fără stare: clienți de rețea, cache-uri, loggeri.
factory — fiecare apel get() creează o nouă instanță. Se aplică pentru ViewModel, repositoare și obiecte cu stare unde este importantă o instanță proaspătă la fiecare accesare.
Integrarea Koin într-un proiect Android este minimă: este suficient să adăugați dependența în build.gradle și să apelați startKoin în Application.onCreate. Koin oferă module pentru integrare cu Jetpack Compose, Navigation și WorkManager, ceea ce îi face o alternativă completă la Hilt.
Biblioteca specială koin-android-compose permite injectarea dependențelor direct în funcțiile Composable prin funcțiile koinViewModel() și koinInject(). Aceasta elimină necesitatea transmiterii containerului prin parametrii fiecărui ecran și face codul ViewModel mai curat datorită legăturii automate cu ciclul de viață.
Conform Google I/O 2024, Jetpack Compose a devenit framework-ul principal pentru proiectele Android noi. Koin asigură suport nativ Compose fără configurare suplimentară, legând automat scopurile de ciclul de viață ViewModel prin koinViewModel() ținând cont de contextul coroutine.
Pentru testare, Koin oferă funcțiile koinTest și koinTestRule care creează un container izolat cu module de test și îl închid automat după terminarea testului. Aceasta asigură izolarea testelor și previne scurgerile de stare între cazurile de test.
Integrarea Koin cu Jetpack Navigation se realizează prin modulul koin-androidx-navigation. ViewModel-ul fiecărui ecran primește automat dependențele prin by viewModel() cu transmiterea SavedStateHandle pentru păstrarea stării la rotirea ecranului și restaurarea după minimizarea aplicației.
Pentru testarea unitară a ViewModel cu Koin se utilizează koinTestRule din biblioteca koin-test-junit5 sau koin-test-junit4. Regula creează un container izolat cu module de test înaintea fiecărui test și îl închide automat după finalizare, prevenind scurgerile de stare între cazurile de test. Dependențele reale sunt înlocuite cu mock-uri prin MockK: modulul cu înregistrări single
Una dintre capacitățile cheie ale Koin 3.x este suportul pentru Ktor pentru crearea de aplicații server în Kotlin și Compose Multiplatform pentru aplicații desktop. Acest lucru face din Koin singurul framework DI care acoperă toate cele trei platforme Kotlin fără schimbarea paradigmei de injectare. Modulul koin-ktor permite înregistrarea dependențelor prin install(Koin) în blocul Application și injectarea serviciilor în rute prin by inject() la fel ca în Android. Acest lucru face din Koin o soluție universală DI pentru proiecte Kotlin de orice arhitectură — de la client mobil la backend server.
Integrarea Koin cu Jetpack Navigation prin modulul koin-androidx-navigation elimină necesitatea creării manuale a ViewModelProvider.Factory pentru fiecare ecran. Pentru proiecte multi-modul, Koin suportă încărcarea lentă a modulelor prin loadKoinModules, permițând conectarea configurației DI a fiecărui modul de feature în mod independent.
Întrebări frecvente
Koin funcționează în runtime fără generare de cod și adnotări, ceea ce accelerează construirea, dar nu verifică graful de dependențe la compilare. Hilt generează cod la compilare și detectează erorile DI mai devreme, dar necesită o configurare complexă și încetinește build-ul.
Da, Koin suportă complet Kotlin Multiplatform. Biblioteca koin-core funcționează pe toate platformele Kotlin, iar koin-android și koin-compose adaugă capacități specifice platformei pentru Android și iOS.
Dependențele ciclice duc la StackOverflowError în runtime. Koin nu le detectează automat. Soluția — refactorizarea arhitecturii: extragerea unei interfețe comune, utilizarea pattern-ului Listener/Observer sau ruperea ciclului printr-o fabrică cu inițializare întârziată.
În Android, scopurile pot fi legate de ciclul de viață al Activity sau Fragment prin AndroidScope. La distrugerea componentului, Koin închide automat scopul corespunzător. În scopurile personalizate (sesiune de utilizator), închiderea se realizează manual prin apelul scope.close.
Utilizați funcția koinTest din modulul koin-test. Aceasta creează un container izolat cu module de test care se închide automat după test. Dependențele reale sunt înlocuite cu mock-uri printr-un modul cu Mockito sau MockK.
Concluzii
Vom dezvolta o aplicație mobilă la cheie
IT Sectr creează aplicații iOS și Android pentru startup-uri și afaceri din 2017. Vă vom consilia și vă vom propune cea mai bună soluție.
Citiți și