Koin — ce este, principiile Dependency Injection și cum funcționează

Autor: IT Sectr Publicat: 2026-05-04 Timp de citire: 8 min

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 care utilizează DSL fără reflecție și generare de cod.
  • Modul — gruparea logică a înregistrărilor de dependențe prin funcțiile single și factory.
  • single — înregistrarea unui singleton, a cărui instanță este creată o singură dată.
  • factory — înregistrare prin care fiecare cerere creează o nouă instanță.
  • Scope — mecanism de asociere a duratei de viață a dependenței de un component, de exemplu de Activity.

Ce este Koin și cu ce se diferențiază

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.

Cum funcționează containerul și DSL-ul Koin

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.

startKoin și module

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.

kotlin
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.

DSL și funcții inline

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.

Metode de injectare a dependențelor în Koin

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.

by inject() — injectare lentă

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.

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

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

get() — obținere explicită

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.

Module și scopuri în Koin

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.

scope — asociere la component

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.

kotlin
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 vs factory

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.

Koin în proiectele Android

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 { mockk() } suprascrie modulul principal, iar ViewModel în test primește un comportament previzibil al dependențelor.

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

Cu ce se diferențiază Koin de Dagger Hilt?

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.

Suportă Koin Kotlin Multiplatform?

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.

Cum se gestionează dependențele ciclice în Koin?

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ă.

Trebuie închise scopurile manual?

Î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.

Cum se testează codul cu Koin?

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

  • Koin — framework DI pentru Kotlin cu DSL fără reflecție, adnotări și generare de cod.
  • Modulele grupează înregistrările prin single (singleton) și factory (instanță nouă).
  • startKoin inițializează containerul global accesibil prin by inject() și get().
  • Scope leagă durata de viață a dependențelor de ciclul de viață al componentului Android.
  • Injectarea în Compose se realizează prin koinViewModel() și koinInject().
  • Koin suportă Kotlin Multiplatform, permițând o abordare DI unitară pe toate platformele.
  • Erorile DI sunt detectate în runtime, de aceea acoperirea cu teste a configurației este obligatorie.

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.

Discutați proiectul

Citiți și