Koin — cos'è, principi di Dependency Injection e come funziona

Autore: IT Sectr Pubblicato: 2026-05-04 Tempo di lettura: 8 min

Koin è un framework DI per Kotlin che funziona senza generazione di codice, riflessione o annotazioni. La libreria utilizza DSL per descrivere i moduli e inietta le dipendenze attraverso un contenitore leggero con supporto per Android, Ktor e Multiplatform. Secondo la documentazione ufficiale di Koin, il framework fornisce moduli, scope e supporto integrato per Jetpack Compose con minimo boilerplate.

Punti chiave

  • Koin è un framework DI per Kotlin che utilizza DSL senza riflessione o generazione di codice.
  • Modulo è un raggruppamento logico di registrazioni di dipendenze tramite le funzioni single e factory.
  • single registra un singleton la cui istanza viene creata una sola volta.
  • factory crea una nuova istanza ad ogni richiesta.
  • Scope è un meccanismo che lega il ciclo di vita della dipendenza a un componente, come un'Activity.

Cos'è Koin e in cosa si differenzia

Koin è un framework DI per Kotlin scritto in linguaggio puro senza utilizzare riflessione, annotazioni o generazione di codice. A differenza di Dagger Hilt, che richiede un processore di annotazioni e generazione di codice in fase di compilazione, Koin funziona esclusivamente a runtime utilizzando un DSL leggero per descrivere i moduli.

L'idea principale di Koin è fornire un'API semplice per registrare e risolvere le dipendenze senza dover apprendere concetti complessi di grafi di dipendenze e alberi di componenti. Lo sviluppatore descrive quali classi sono disponibili per il contenitore e Koin le inietta automaticamente attraverso il costruttore o delegati lazy by inject. Il framework è completamente compatibile con Kotlin Multiplatform, consentendo un approccio DI unificato su Android, iOS e lato server.

Secondo il sondaggio della Kotlin Developers Community (2025), Koin è utilizzato nel 31% dei progetti Android commerciali, secondo solo a Hilt (47%). Il motivo principale della scelta è la facilità di configurazione e l'assenza di necessità di generazione di codice, che accelera la compilazione del progetto.

Scegli Koin per progetti medi e grandi dove è importante un avvio rapido dello sviluppo, o per soluzioni Kotlin Multiplatform dove Hilt non è disponibile per ragioni architetturali.

Koin non utilizza riflessione o generazione di codice — tutte le registrazioni si basano su funzioni inline con tipi reified che sostituiscono il tipo concreto nel corpo della funzione in fase di compilazione. Questo rende Koin uno dei framework DI più leggeri in termini di dimensione finale dell'APK: aggiungere Koin aumenta la dimensione dell'applicazione di soli 100-150 KB, mentre Dagger Hilt aggiunge circa 500 KB a causa del codice generato.

Come funzionano il contenitore e il DSL di Koin

Il contenitore Koin viene inizializzato tramite la funzione startKoin, che accetta una lambda con configurazione. All'interno di questa lambda vengono descritti i moduli con le registrazioni — il blocco di costruzione principale della logica DI.

startKoin e moduli

La funzione startKoin crea un contenitore globale accessibile da qualsiasi punto dell'applicazione tramite GlobalContext, tuttavia nei progetti multi-modulo si raccomanda di utilizzare KoinApplication per creare contenitori isolati. In Android, per l'inizializzazione viene utilizzato AndroidContext, che viene automaticamente legato al ciclo di vita di Application. I moduli vengono registrati tramite il parametro modules, che accetta una lista di istanze di Module.

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

startKoin {
    modules(networkModule)
}

Ogni modulo contiene definizioni tramite single (singleton) o factory (nuova istanza). Le definizioni possono fare riferimento ad altre dipendenze registrate tramite get(), formando un grafo di iniezione senza specifica esplicita del tipo e senza codice boilerplate.

DSL e funzioni inline

Koin utilizza attivamente funzioni inline con parametri reified per l'inferenza del tipo dal contesto. Ciò consente di scrivere registrazioni senza specificare esplicitamente la classe: single { MyService() } determina automaticamente il tipo dal valore di ritorno della lambda.

A differenza di Dagger, Koin non verifica il grafo delle dipendenze in fase di compilazione — tutti gli errori vengono rilevati a runtime al primo accesso a una dipendenza non risolta. Questo è un compromesso che semplifica notevolmente il codice e accelera le compilazioni, ma richiede una copertura di test della configurazione DI. Molti team scelgono Koin proprio per la velocità di sviluppo e la semplicità, nonostante l'assenza di controlli in fase di compilazione.

Nella versione Koin 3.5, è apparso il controllo sperimentale del grafo in fase di compilazione tramite il plugin Koin Annotations. Lo sviluppatore aggiunge le annotazioni @Module e @KoinComponent, e il plugin genera codice di validazione che viene eseguito durante la compilazione. Tuttavia, il principale vantaggio di Koin — assenza di generazione di codice — viene perso in questa modalità, quindi la maggior parte dei team continua a utilizzare l'approccio DSL classico con controlli a runtime tramite test.

Metodi di iniezione delle dipendenze in Koin

Koin fornisce diversi modi per iniettare dipendenze: by inject(), get() e passaggio diretto tramite costruttore. La scelta dipende dal contesto di utilizzo.

by inject() — iniezione lazy

Il delegato by inject è il metodo di iniezione più comune in ViewModel e fragment Android. La dipendenza viene inizializzata in modo lazy — solo al primo accesso alla proprietà. Ciò è efficiente per servizi che consumano molte risorse che potrebbero non essere necessari immediatamente.

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

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

get() — ottenimento esplicito

La funzione get restituisce immediatamente un'istanza di dipendenza. Viene utilizzata all'interno di lambda factory durante la registrazione o quando la dipendenza è necessaria in un contesto sincrono senza inizializzazione lazy. A differenza di by inject(), get() non supporta il caricamento lazy e richiede che il contenitore sia già inizializzato al momento della chiamata.

Moduli e scope in Koin

Scope in Koin è un meccanismo per legare il ciclo di vita delle dipendenze a un componente specifico, come un'Activity, un Fragment o una sessione personalizzata. Questa è una funzionalità chiave per la gestione della memoria nelle applicazioni Android.

scope — legame a un componente

La funzione scope all'interno di un modulo crea uno scope che vive finché vive il componente legato. Tutte le dipendenze registrate nello scope vengono distrutte quando viene chiuso, prevenendo perdite di memoria.

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

La funzione scoped registra una dipendenza che esisterà solo all'interno dello scope. Quando lo scope viene chiuso, tutti gli oggetti scoped diventano disponibili per il garbage collector.

single vs factory

single registra una singola istanza per l'intera applicazione con inizializzazione lazy. Utilizzato per servizi senza stato: client di rete, cache, logger.

factory crea una nuova istanza ad ogni chiamata get(). Applicato a ViewModel, repository e oggetti con stato dove è importante un'istanza fresca ad ogni accesso.

Koin nei progetti Android

Integrare Koin in un progetto Android è minimale: basta aggiungere una dipendenza in build.gradle e chiamare startKoin in Application.onCreate. Koin fornisce moduli per l'integrazione con Jetpack Compose, Navigation e WorkManager, rendendolo un'alternativa completa a Hilt.

La libreria speciale koin-android-compose consente di iniettare dipendenze direttamente nelle funzioni Composable tramite koinViewModel() e koinInject(). Ciò elimina la necessità di passare il contenitore attraverso i parametri di ogni schermata e rende il codice ViewModel più pulito grazie al legame automatico al ciclo di vita.

Secondo Google I/O 2024, Jetpack Compose è diventato il framework principale per i nuovi progetti Android. Koin fornisce supporto nativo per Compose senza configurazione aggiuntiva, legando automaticamente gli scope al ciclo di vita del ViewModel tramite koinViewModel() tenendo conto del contesto di coroutine.

Per i test, Koin fornisce le funzioni koinTest e koinTestRule, creando un contenitore di test isolato con moduli di test e chiudendolo automaticamente al termine del test. Ciò garantisce l'isolamento dei test e previene perdite di stato tra casi di test.

L'integrazione di Koin con Jetpack Navigation è implementata tramite il modulo koin-androidx-navigation. Il ViewModel di ogni schermata riceve automaticamente le dipendenze tramite by viewModel() con passaggio di SavedStateHandle per preservare lo stato durante la rotazione dello schermo e il recupero dopo la sospensione dell'applicazione.

Per i test unitari di ViewModel con Koin, si utilizza koinTestRule della libreria koin-test-junit5 o koin-test-junit4. La regola crea un contenitore isolato con moduli di test prima di ogni test e lo chiude automaticamente al termine, prevenendo perdite di stato tra casi di test. Le dipendenze reali vengono sostituite con mock tramite MockK: un modulo con registrazioni single { mockk() } sovrascrive il modulo principale, e il ViewModel nel test ottiene un comportamento prevedibile delle dipendenze.

Una delle caratteristiche principali di Koin 3.x è il supporto per Ktor per creare applicazioni server in Kotlin e Compose Multiplatform per applicazioni desktop. Ciò rende Koin l'unico framework DI che copre tutte e tre le piattaforme Kotlin senza cambiare il paradigma di iniezione. Il modulo koin-ktor consente di registrare dipendenze tramite install(Koin) nel blocco Application e di iniettare servizi nelle route tramite by inject() come in Android. Ciò rende Koin una soluzione DI universale per progetti Kotlin di qualsiasi architettura — dal client mobile al backend server.

L'integrazione di Koin con Jetpack Navigation tramite il modulo koin-androidx-navigation elimina la necessità di creare manualmente un ViewModelProvider.Factory per ogni schermata. Per progetti multi-modulo, Koin supporta il caricamento lazy dei moduli tramite loadKoinModules, consentendo a ciascun modulo di funzionalità di connettere la propria configurazione DI in modo indipendente.

Domande frequenti

In cosa si differenzia Koin da Dagger Hilt?

Koin funziona a runtime senza generazione di codice o annotazioni, accelerando le compilazioni ma non verificando il grafo delle dipendenze in fase di compilazione. Hilt genera codice in fase di compilazione e rileva gli errori DI prima, ma richiede una configurazione complessa e rallenta la compilazione.

Koin supporta Kotlin Multiplatform?

Sì, Koin supporta completamente Kotlin Multiplatform. La libreria koin-core funziona su tutte le piattaforme Kotlin, mentre koin-android e koin-compose aggiungono capacità specifiche della piattaforma per Android e iOS rispettivamente.

Come gestire le dipendenze circolari in Koin?

Le dipendenze circolari portano a StackOverflowError a runtime. Koin non le rileva automaticamente. La soluzione è il refactoring dell'architettura: estrazione di un'interfaccia comune, utilizzo del pattern Listener/Observer o rottura del ciclo tramite una factory con inizializzazione differita.

È necessario chiudere gli scope manualmente?

In Android, gli scope possono essere legati al ciclo di vita di Activity o Fragment tramite AndroidScope. Quando il componente viene distrutto, Koin chiude automaticamente lo scope corrispondente. Negli scope personalizzati (sessione utente), la chiusura viene eseguita manualmente chiamando scope.close.

Come testare il codice con Koin?

Utilizza la funzione koinTest del modulo koin-test. Crea un contenitore isolato con moduli di test che si chiude automaticamente dopo il test. Le dipendenze reali vengono sostituite con mock tramite un modulo con Mockito o MockK.

Riepilogo

  • Koin è un framework DI per Kotlin con DSL senza riflessione, annotazioni o generazione di codice.
  • I moduli raggruppano le registrazioni tramite single (singleton) e factory (nuova istanza).
  • startKoin inizializza un contenitore globale accessibile tramite by inject() e get().
  • Scope lega il ciclo di vita delle dipendenze al ciclo di vita del componente Android.
  • L'iniezione in Compose viene effettuata tramite koinViewModel() e koinInject().
  • Koin supporta Kotlin Multiplatform, consentendo un approccio DI unificato su tutte le piattaforme.
  • Gli errori DI vengono rilevati a runtime, quindi la copertura di test della configurazione è obbligatoria.

Svilupperemo un'applicazione mobile chiavi in mano

IT Sectr crea applicazioni iOS e Android per startup e aziende dal 2017. Ti consulteremo e ti proporremo la soluzione migliore.

Discuti il progetto

Leggi anche