# Android Dependency Injection: Hilt vs Koin – Guida Completa e Domande da Colloquio 2026 > Confronto completo tra Hilt e Koin per la Dependency Injection su Android con esempi di codice, benchmark e domande frequenti nei colloqui tecnici 2026. - Published: 2026-05-24 - Updated: 2026-05-24 - Author: SharpSkill - Tags: android, kotlin, dependency-injection, hilt, koin - Reading time: 12 min --- La Dependency Injection (DI) rappresenta uno dei principi architetturali fondamentali nello sviluppo Android. Invece di creare le dipendenze direttamente all'interno di una classe, queste vengono fornite dall'esterno, migliorando testabilità, manutenibilità e modularità del codice. Nel 2026, due framework dominano questo ambito: [Hilt](https://dagger.dev/hilt/) alla versione 2.57.1, la soluzione ufficiale di Google basata su Dagger con validazione a compile-time, e [Koin](https://insert-koin.io/) alla versione 4.2.1, un service locator leggero che utilizza una DSL Kotlin pura per la risoluzione a runtime. La scelta tra i due influenza tempi di build, performance all'avvio, testabilità e velocità di onboarding dei nuovi membri del team. > **Compile-Time vs Runtime DI** > > Hilt valida l'intero grafo delle dipendenze durante la compilazione e blocca il build se manca un binding. Koin risolve le dipendenze a runtime attraverso una DSL Kotlin, intercettando gli errori di configurazione solo quando il percorso di codice interessato viene eseguito. Questa differenza fondamentale determina ogni compromesso tra i due framework. ## Come funziona Hilt sotto il cofano Hilt genera componenti Dagger mappati sulle classi del lifecycle Android. L'annotazione `@HiltAndroidApp` attiva la generazione di codice a compile-time, producendo factory e provider per ogni binding dichiarato. KSP (Kotlin Symbol Processing) ha sostituito KAPT come processore di annotazioni raccomandato a partire da Hilt 2.48, dimezzando approssimativamente i tempi di elaborazione. Un setup Hilt tipico coinvolge tre elementi: un modulo che dichiara i binding, un entry point (Activity, Fragment o ViewModel) e la classe Application annotata con `@HiltAndroidApp`. ```kotlin // AppModule.kt @Module @InstallIn(SingletonComponent::class) object AppModule { @Provides @Singleton fun provideRetrofit(): Retrofit { return Retrofit.Builder() .baseUrl("https://api.example.com/") .addConverterFactory(GsonConverterFactory.create()) .build() } @Provides @Singleton fun provideUserRepository( retrofit: Retrofit ): UserRepository { return UserRepositoryImpl(retrofit.create(UserApi::class.java)) } } ``` ```kotlin // UserViewModel.kt @HiltViewModel class UserViewModel @Inject constructor( private val userRepository: UserRepository ) : ViewModel() { private val _users = MutableStateFlow>(emptyList()) val users: StateFlow> = _users.asStateFlow() fun loadUsers() { viewModelScope.launch { _users.value = userRepository.getUsers() } } } ``` Il compilatore verifica che esista un provider valido per `UserRepository` prima che l'app venga mai eseguita. I binding mancanti emergono come errori di build, non come crash a runtime. ## Setup di Koin e l'approccio DSL Kotlin Koin adotta l'approccio opposto: zero generazione di codice, zero elaborazione di annotazioni. Le dipendenze vengono dichiarate in Kotlin puro usando una DSL, e il framework le risolve a runtime attraverso un registro di servizi globale. Koin 4.2 ha introdotto i Lazy Modules per il caricamento parallelo all'avvio e un nuovo motore `CoreResolverV2` che ottimizza la risoluzione degli scope. ```kotlin // AppModule.kt val appModule = module { // Istanza Singleton di Retrofit single { Retrofit.Builder() .baseUrl("https://api.example.com/") .addConverterFactory(GsonConverterFactory.create()) .build() } // Repository singleton legato alla sua interfaccia single { UserRepositoryImpl(get().create(UserApi::class.java)) } // ViewModel con repository iniettato viewModel { UserViewModel(get()) } } ``` ```kotlin // MyApplication.kt class MyApplication : Application() { override fun onCreate() { super.onCreate() startKoin { androidContext(this@MyApplication) modules(appModule) } } } ``` La DSL si legge in modo naturale e non richiede alcun plugin di annotation processing nella configurazione Gradle. Il compromesso: un errore di battitura nella risoluzione `get()` si manifesta solo a runtime quando quel punto di iniezione specifico viene eseguito. ## Benchmark delle performance: tempo di build vs costo a runtime La questione delle performance emerge in quasi ogni [colloquio Android](/technologies/android/interview-questions/android-dependency-injection). I numeri concreti contano più delle opinioni. | Metrica | Hilt 2.57 (KSP) | Koin 4.2 | |---------|-----------------|----------| | Overhead clean build | +8-15s (generazione codice KSP) | ~0s (nessuna generazione) | | Overhead build incrementale | +2-4s | ~0s | | Avvio app (50 binding) | ~0ms (codice pre-generato) | ~5-15ms (risoluzione grafo) | | Avvio app (500 binding) | ~0ms | ~30-80ms | | Impatto dimensione APK | +200-400 KB (codice generato) | +100 KB (libreria runtime) | | Rilevamento binding mancanti | Errore di compilazione | Crash a runtime | Per progetti con meno di 100 binding, l'overhead a runtime di Koin resta impercettibile. Oltre i 300 binding, il costo all'avvio diventa misurabile, sebbene i Lazy Modules di Koin 4.2 mitighino questo aspetto parallelizzando il caricamento dei moduli. > **KSP vs KAPT** > > Passare da KAPT a KSP per l'elaborazione Hilt riduce i tempi di annotation processing del 40-60%. Qualsiasi progetto che utilizza ancora KAPT con Hilt dovrebbe migrare immediatamente a KSP. Hilt supporta KSP dalla versione 2.48. ## Strategie di testing con ciascun framework La testabilità è l'ambito in cui le differenze architetturali diventano concrete. Hilt fornisce `@TestInstallIn` per sostituire i moduli di produzione con test double a compile-time. Koin offre `loadKoinModules()` per sovrascrivere le definizioni a runtime. ```kotlin // Modulo di test Hilt — sostituisce i binding di AppModule nei test @Module @TestInstallIn( components = [SingletonComponent::class], replaces = [AppModule::class] ) object FakeAppModule { @Provides @Singleton fun provideUserRepository(): UserRepository { return FakeUserRepository() // Test double in memoria } } ``` ```kotlin // Test Koin — sovrascrittura a runtime class UserViewModelTest : KoinTest { @Before fun setUp() { startKoin { modules( module { single { FakeUserRepository() } viewModel { UserViewModel(get()) } } ) } } @Test fun `loads users from repository`() { val viewModel: UserViewModel = get() viewModel.loadUsers() assertEquals(3, viewModel.users.value.size) } @After fun tearDown() { stopKoin() } } ``` I test con Hilt intercettano moduli di test mal configurati a compile-time. I test con Koin richiedono una gestione attenta del lifecycle (`startKoin`/`stopKoin`), ma offrono maggiore flessibilità per sovrascritture parziali dei moduli. ## Architettura multi-modulo: scalare la DI attraverso le feature I grandi progetti Android suddividono le feature in moduli Gradle separati. Il framework DI deve supportare i confini tra moduli in modo pulito. Con Hilt, ogni feature module dichiara il proprio `@Module` annotato con `@InstallIn`. Hilt unifica tutti i moduli in una singola gerarchia di componenti a compile-time. L'[architettura MVVM](/blog/android/mvvm-vs-mvi-architecture) si integra naturalmente con i componenti con scope di Hilt. ```kotlin // feature-payments/PaymentModule.kt @Module @InstallIn(ViewModelComponent::class) object PaymentModule { @Provides fun providePaymentGateway( retrofit: Retrofit // Fornito dal modulo :app ): PaymentGateway { return StripePaymentGateway(retrofit.create(PaymentApi::class.java)) } } ``` Con Koin, i feature module esportano le loro dichiarazioni `module {}`, e il modulo app le carica tutte all'avvio. I Lazy Modules di Koin 4.2 consentono il caricamento differito dei feature module fino al primo accesso. ```kotlin // feature-payments/paymentModule.kt val paymentModule = module { factory { StripePaymentGateway(get().create(PaymentApi::class.java)) } } // app/MyApplication.kt — carica tutti i feature module startKoin { modules(appModule, paymentModule, analyticsModule) } ``` Hilt impone la visibilità delle dipendenze attraverso gli scope dei componenti. Koin si affida alle convenzioni — qualsiasi modulo può accedere alle definizioni di qualsiasi altro modulo, il che offre flessibilità ma può portare a un accoppiamento implicito. ## Considerazioni su Kotlin Multiplatform Koin supporta [Kotlin Multiplatform](https://kotlinlang.org/docs/multiplatform.html) (KMP) nativamente. La stessa DSL `module {}` funziona su Android, iOS, Desktop e Web. Hilt è esclusivamente per Android — dipende da componenti lifecycle specifici di Android e dal processore di annotazioni Dagger. Per i progetti che mirano a più piattaforme, Koin è l'unica opzione praticabile tra i due. I moduli di business logic condivisi possono dichiarare le proprie dipendenze una sola volta e iniettarle su tutti i target. ## Domande frequenti nei colloqui tecnici sulla DI Android Queste domande emergono regolarmente nei [colloqui tecnici Android](/technologies/android). Ogni risposta resta concisa e focalizzata su ciò che gli intervistatori si aspettano. **D: Qual è la differenza tra dependency injection a compile-time e a runtime?** La DI a compile-time (Hilt/Dagger) genera il codice di iniezione durante il build. Il compilatore valida l'intero grafo delle dipendenze, intercettando i binding mancanti prima del runtime. La DI a runtime (Koin) risolve le dipendenze quando vengono richieste per la prima volta, usando un pattern service registry. La DI a compile-time produce un avvio più rapido ma build più lenti; la DI a runtime non ha overhead sul build ma rinvia il rilevamento degli errori. **D: Quali scope dei componenti Hilt esistono e come si mappano ai lifecycle Android?** `SingletonComponent` vive per l'intera durata dell'applicazione. `ActivityRetainedComponent` sopravvive ai cambiamenti di configurazione. `ViewModelComponent` è legato al lifecycle di un ViewModel. `ActivityComponent`, `FragmentComponent` e `ViewComponent` seguono i rispettivi lifecycle owner Android. Scope personalizzati possono estendere questa gerarchia. **D: Come gestisce Koin l'iniezione dei ViewModel in Jetpack Compose?** Koin fornisce `koinViewModel()` come funzione Composable che crea o recupera un ViewModel con scope al `ViewModelStoreOwner` più vicino. Da Koin 4.2, `koinNavViewModel()` vincola i ViewModel alle voci del navigation graph quando si utilizza [Jetpack Compose Navigation](/blog/android/jetpack-compose-interview-questions). **D: Quando Koin sarebbe una scelta migliore rispetto a Hilt?** Koin si adatta meglio ai progetti KMP (Hilt è solo per Android), alle app di piccole e medie dimensioni dove il tempo di build conta più del tempo di avvio, e ai team che preferiscono una DSL Kotlin esplicita rispetto alla configurazione basata su annotazioni. Anche i progetti di prototyping e proof-of-concept beneficiano del setup minimale di Koin. > **Errore comune nei colloqui** > > Definire Koin come un "framework di dependency injection" è tecnicamente impreciso. Koin è un service locator — le dipendenze vengono estratte tramite `get()` anziché iniettate tramite constructor injection. Gli intervistatori che conoscono questa distinzione si aspettano che i candidati la riconoscano. Hilt/Dagger esegue una vera dependency injection attraverso chiamate al costruttore generate. ## Schema decisionale: scegliere tra Hilt e Koin | Fattore | Hilt | Koin | |---------|------|------| | Validazione del grafo | Compile-time | Runtime | | Impatto sul tempo di build | Maggiore (codegen KSP) | Nessuno | | Costo di avvio | Quasi zero | Scala con la dimensione del grafo | | Curva di apprendimento | Più ripida (concetti Dagger) | Dolce (pura DSL Kotlin) | | Supporto KMP | No | Sì | | Integrazione Jetpack | Profonda (ufficiale Google) | Buona (community) | | Scalabilità del team | Più forte (scope imposti) | Flessibile (basato su convenzioni) | | Testing | `@TestInstallIn` (compile-safe) | `loadKoinModules` (runtime) | Nessuno dei due framework è universalmente superiore. La scelta dipende dalla dimensione del progetto, dall'esperienza del team e dalle piattaforme target. ## Conclusione - Hilt 2.57 con KSP offre validazione del grafo a compile-time e costo di avvio quasi nullo, rendendolo la scelta più solida per grandi progetti Android single-platform con requisiti di affidabilità rigorosi - Koin 4.2 con Lazy Modules e `CoreResolverV2` colma il divario nelle performance di avvio mantenendo zero overhead di build e piena compatibilità KMP - Entrambi i framework gestiscono architetture multi-modulo, ma Hilt impone i confini degli scope a compile-time mentre Koin si affida alle convenzioni del team - Per la [preparazione ai colloqui Android](/technologies/android), comprendere il compromesso compile-time vs runtime e saper articolare quando ciascun framework è più adatto rappresenta la base attesa - Migrare tra i due framework non è banale — la scelta iniziale dovrebbe tenere conto della traiettoria di piattaforma e scalabilità a lungo termine del progetto --- Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack. HTML version of this page: https://sharpskill.dev/it/blog/android/android-dependency-injection-hilt-vs-koin