Dagger / Hilt: cos'è, DI e applicazione

Autore: IT Sectr Pubblicato: 2026-05-03 Tempo di lettura: 9 min

Dagger è un framework di dependency injection per Java e Kotlin che genera codice DI in fase di compilazione attraverso l'elaborazione di annotazioni. Hilt è un wrapper su Dagger per Android che semplifica la configurazione dei componenti e la gestione del ciclo di vita. Secondo Google, 2025, Hilt è utilizzato in oltre il 70% delle app Android della Top-100 di Google Play, supportando Activity, Fragment, ViewModel e Service tramite componenti predefiniti. Entrambi i framework forniscono verifica del grafo delle dipendenze in fase di compilazione, eliminando errori di iniezione a runtime.

Punti chiave

  • Dagger — framework DI a compile-time con generazione di codice tramite annotazioni @Module, @Provides, @Component
  • Hilt — wrapper Android che semplifica Dagger tramite @HiltAndroidApp, @AndroidEntryPoint, @HiltViewModel
  • Component — grafo delle dipendenze che collega Module ai target Inject tramite metodi proxy
  • Scope — @Singleton, @ViewModelScoped, @ActivityScoped gestiscono la durata degli oggetti iniettati
  • Hilt supporta progetti multi-modulo tramite @InstallIn per grafi di dipendenze isolati

Cos'è Dagger / Hilt?

Dagger è un framework di dependency injection con generazione di codice a compile-time. Sviluppato inizialmente in Square e successivamente trasferito a Google, Dagger utilizza il processore di annotazioni Java APT per analizzare il grafo delle dipendenze e generare classi factory. A differenza del DI a runtime (Guice, Koin), Dagger non usa reflection — tutto il codice viene creato in compilazione, garantendo le massime prestazioni a runtime e il rilevamento degli errori in fase di build.

Hilt è una libreria Google costruita su Dagger e ottimizzata per Android. Hilt fornisce componenti predefiniti corrispondenti al ciclo di vita dei componenti Android: @SingletonComponent per Application, @ActivityComponent per Activity, @FragmentComponent per Fragment, @ViewModelComponent per ViewModel. Questo elimina la configurazione di routine di Component e Module richiesta in Dagger puro. Hilt genera inoltre automaticamente il grafo delle dipendenze per ogni componente Android tramite @AndroidEntryPoint.

Secondo Google I/O 2024, Hilt è la soluzione consigliata per DI nelle applicazioni Android scritte in Kotlin. Le librerie Jetpack (Navigation, Room, WorkManager) hanno un'integrazione incorporata con Hilt tramite @HiltViewModel e @HiltWorker. Nei progetti che non usano Android (librerie Java/Kotlin pure, applicazioni server), si usa Dagger puro senza il wrapper Hilt.

Il problema dell'iniezione manuale delle dipendenze

Senza un framework DI, lo sviluppatore crea gli oggetti manualmente tramite costruttori o factory, passando le dipendenze lungo la catena. Ogni nuovo requisito significa modificare le firme di tutti i costruttori nella catena. Dagger automatizza questo processo: basta dichiarare il tipo necessario (@Inject constructor), e Dagger crea il grafo delle dipendenze, risolvendo tutti i tipi annidati. Quando le dipendenze cambiano, Dagger aggiorna automaticamente il codice generato — è impossibile sbagliare nella catena.

Principi della dependency injection

Dependency Injection è un pattern in cui un oggetto riceve le sue dipendenze dall'esterno anziché crearle da sé. DI implementa il principio di Inversione del Controllo (IoC): una classe non è responsabile della creazione delle proprie dipendenze, ma le dichiara tramite un costruttore, un metodo o un campo. L'iniezione tramite costruttore è considerata la più preferibile perché garantisce che l'oggetto venga creato in uno stato valido.

Tipo di iniezioneSintassi DaggerQuando usare
Constructor injection@Inject constructorMetodo principale — per tutte le classi personalizzate
Field injection@Inject lateinit varSolo per componenti Android (Activity, Fragment)
Method injection@Inject fun bind()Per inizializzazione post-costruzione

Vantaggi del DI a compile-time

I principali vantaggi del DI includono testabilità (le dipendenze possono essere sostituite con oggetti mock), basso accoppiamento (le classi dipendono da interfacce, non da implementazioni) e gestione esplicita del ciclo di vita degli oggetti tramite scope. Dagger garantisce automaticamente che un oggetto venga creato una volta all'interno del suo scope e distrutto all'uscita dallo scope.

Architettura di Dagger: Component, Module, Provides

Component è l'elemento centrale del grafo delle dipendenze di Dagger. È un'interfaccia annotata con @Component che descrive il ponte tra Module e i target di iniezione. Dagger genera l'implementazione di Component (ad esempio DaggerAppComponent) in fase di compilazione. Il Component determina quali tipi sono disponibili per l'iniezione tramite metodi astratti che restituiscono i tipi richiesti o tramite metodi inject che accettano un oggetto per l'iniezione di campo.

kotlin
// Module: fornisce dipendenze che Dagger non può creare da solo
@Module
class NetworkModule {
    @Provides
    @Singleton
    fun provideOkHttpClient(): OkHttpClient {
        return OkHttpClient.Builder()
            .connectTimeout(30, TimeUnit.SECONDS)
            .build()
    }

    @Provides
    @Singleton
    fun provideApiService(client: OkHttpClient): ApiService {
        return Retrofit.Builder()
            .baseUrl("https://api.example.com/")
            .client(client)
            .addConverterFactory(GsonConverterFactory.create())
            .build()
            .create(ApiService::class.java)
    }
}

// Component: collega Module e target di Injection
@Component(modules = [NetworkModule::class])
interface AppComponent {
    fun inject(activity: MainActivity)
    fun getApiService(): ApiService
}

@Module è una classe contenente metodi con @Provides che restituiscono istanze di dipendenze. Module viene usato per i tipi che Dagger non può creare automaticamente: librerie di terze parti (OkHttp, Retrofit), oggetti con parametri del costruttore, interfacce con selezione di implementazione. @Binds è un'alternativa a @Provides per i casi in cui un metodo restituisce un'interfaccia e accetta un'unica implementazione: Dagger genera un cast diretto senza chiamare il metodo.

@Scope definisce la durata di un oggetto nel grafo delle dipendenze. @Singleton — l'oggetto viene creato una volta per l'intera applicazione. @ActivityScoped — l'oggetto vive finché vive l'Activity. @FragmentScoped — finché vive il Fragment. Senza scope, Dagger crea una nuova istanza a ogni iniezione. @Reusable — uno scope per oggetti che non devono essere singleton ma la cui creazione è costosa — Dagger può mettere in cache l'istanza ma non lo garantisce.

Hilt per Android: @HiltAndroidApp e @AndroidEntryPoint

Hilt semplifica la configurazione di Dagger per Android tramite componenti predefiniti e generazione automatica del grafo base. L'annotazione @HiltAndroidApp sulla classe Application attiva la generazione del componente Hilt. Senza questa annotazione, Hilt non funziona — è obbligatoria per qualsiasi applicazione Android che usi Hilt. @HiltAndroidApp crea il componente padre SingletonComponent, da cui ereditano tutti gli altri componenti dell'applicazione.

kotlin
@HiltAndroidApp
class MyApplication : Application()

@AndroidEntryPoint
class MainActivity : AppCompatActivity() {

    @Inject lateinit var apiService: ApiService

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        // apiService già iniettato prima della chiamata onCreate
    }
}

@Module
@InstallIn(SingletonComponent::class)
class AppModule {
    @Provides
    @Singleton
    fun provideDatabase(@ApplicationContext ctx: Context): AppDatabase {
        return Room.databaseBuilder(ctx, AppDatabase::class.java, "app.db").build()
    }
}

@AndroidEntryPoint è un'annotazione per Activity, Fragment, Service, BroadcastReceiver e View. Genera un componente Hilt per ogni tipo: @AndroidEntryPoint su un'Activity crea un ActivityComponent che eredita da SingletonComponent. Il componente figlio riceve automaticamente tutte le dipendenze del padre. L'iniezione di campo con @Inject lateinit var è disponibile solo nelle classi annotate con @AndroidEntryPoint — nelle classi normali si usa l'iniezione tramite costruttore.

@InstallIn specifica in quale componente Hilt viene installato un modulo. NetworkModule con @InstallIn(SingletonComponent::class) è disponibile in tutta l'applicazione. Un Module con @InstallIn(ActivityComponent::class) è disponibile solo in Activity. Questo isola i grafi delle dipendenze: i moduli specifici di Activity non sono visibili in Fragment e ViewModel, prevenendo l'uso accidentale di dipendenze non valide. @ApplicationContext è un qualificatore integrato di Hilt per ottenere il Context dell'applicazione.

Qualifier: @Named e qualificatori personalizzati

Quando è necessario iniettare due diverse implementazioni della stessa interfaccia, si usano i qualificatori. Hilt supporta @Named per identificatori stringa e annotazioni personalizzate con @Qualifier. Ad esempio, @Named("baseUrl") e @Named("imageBaseUrl") per diverse configurazioni stringa. I qualificatori personalizzati sono preferibili a @Named grazie al controllo in fase di compilazione — un nome stringa errato non verrà rilevato fino al runtime.

Hilt ViewModel: @HiltViewModel e @Inject constructor

@HiltViewModel è un'annotazione che sostituisce la factory manuale ViewModelProvider.Factory. Una classe annotata con @HiltViewModel con @Inject constructor riceve automaticamente tutte le dipendenze tramite Dagger. Hilt genera una ViewModelFactory utilizzata da Jetpack ViewModelProvider. Senza Hilt, lo sviluppatore deve scrivere la factory manualmente, passando ogni parametro dall'Activity o dal fragment.

kotlin
@HiltViewModel
class MainViewModel
    @Inject constructor(
        private val apiService: ApiService,
        private val database: AppDatabase
    ) : ViewModel() {

    private val _users = MutableStateFlow<List<User>>(emptyList())
    val users: StateFlow<List<User>> = _users.asStateFlow()

    fun loadUsers() {
        viewModelScope.launch {
            _users.value = apiService.getUsers()
        }
    }
}

// In Activity — Hilt crea automaticamente il ViewModel
@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
    private val viewModel: MainViewModel by viewModels()
}

ViewModelScoped è uno scope di Hilt per dipendenze che vivono finché vive il ViewModel. Se due ViewModel dello stesso tipo iniettano la stessa dipendenza @ViewModelScoped, ciascuno riceve la propria istanza. Questo distingue @ViewModelScoped da @ActivityScoped, dove un'Activity riceve un'istanza per tutti i fragment. Per dipendenze specifiche di ViewModel (ad esempio SavedStateHandle), si usa @HiltViewModel con @Inject constructor(savedStateHandle: SavedStateHandle).

Hilt supporta l'iniezione assistita tramite la libreria Hilt Extensions. L'iniezione assistita consente di passare parametri al costruttore al momento dell'iniezione quando alcune dipendenze sono note solo a runtime (ad esempio, l'ID utente da un intent). Per l'iniezione assistita, si usa @AssistedInject in combinazione con parametri @Assisted. Hilt genera un AssistedFactory che può essere iniettato in modo standard.

Dagger vs Hilt: confronto e migrazione

Dagger puro richiede la creazione manuale di Component, la definizione degli scope e la configurazione dell'iniezione in ogni componente Android. Lo sviluppatore crea AppComponent, ActivityComponent, FragmentComponent e gestisce le loro relazioni tramite @Subcomponent. Questo approccio offre il massimo controllo ma richiede una quantità significativa di codice boilerplate. Dagger viene usato in progetti grandi che richiedono un'architettura DI non standard, o in progetti Java/Kotlin non Android.

Hilt automatizza il boilerplate: un @HiltAndroidApp, un @AndroidEntryPoint per ogni componente, scope predefiniti. Google raccomanda Hilt per tutti i nuovi progetti Android. La migrazione da Dagger a Hilt include la sostituzione di Component con @InstallIn, la sostituzione di @Subcomponent con componenti Hilt predefiniti e la sostituzione della factory manuale ViewModelProvider.Factory con @HiltViewModel. La maggior parte delle classi @Module viene migrata con l'aggiunta di @InstallIn senza modificare i metodi @Provides.

CaratteristicaDaggerHilt
ConfigurazioneManuale: Component, Subcomponent, BuilderAutomatica: @HiltAndroidApp, @AndroidEntryPoint
Componenti AndroidNessun predefinito12+ componenti integrati
ViewModelFactory manuale@HiltViewModel + @Inject constructor
MultimoduloTramite @Component(dependencies)Tramite @InstallIn + aggregazione
ComplessitàAlta — esperienza necessariaBassa — intuitivamente comprensibile
FlessibilitàMassimaStandard (copre il 95% degli scenari)

Limitazioni di Hilt: la libreria supporta solo Android (non adatta a progetti Java puramente lato server), impone una certa struttura di componenti (difficile da sovrascrivere) e aggiunge una dipendenza da android.hilt:hilt-navigation-compose per progetti Jetpack Compose. Per le applicazioni Compose, Hilt fornisce @HiltViewModel accessibile in Composable tramite hiltViewModel() — senza fornitura manuale di ViewModel dall'Activity.

Domande frequenti

Qual è la differenza tra Dagger e Hilt?

Dagger è un framework DI di base a compile-time con configurazione manuale di Component e Module. Hilt è un wrapper Android che automatizza la creazione di componenti e l'integrazione con il ciclo di vita di Activity, Fragment, ViewModel, Service e BroadcastReceiver.

Perché serve @HiltAndroidApp?

@HiltAndroidApp abilita la generazione del componente Hilt per Application. Senza questa annotazione, Hilt non può creare il SingletonComponent base da cui ereditano tutti gli ActivityComponent, FragmentComponent e ViewModelComponent. L'annotazione è obbligatoria per qualsiasi progetto Hilt.

Come funziona Hilt con Jetpack Navigation?

Hilt Navigation fornisce @HiltViewModel per ViewModel in NavBackStackEntry e hiltNavGraphViewModels() per lo scoping di ViewModel all'interno del grafo di navigazione. La libreria android.hilt:hilt-navigation-fragment crea automaticamente un ViewModel per ogni NavBackStackEntry.

Come iniettare Context in Hilt?

Usa @ApplicationContext per il contesto dell'applicazione o @ActivityContext per il contesto dell'Activity. Hilt fornisce questi qualificatori integrati nella libreria android.hilt:hilt-android. @ActivityContext è disponibile solo nei moduli installati in ActivityComponent.

Cos'è @Binds e quando usarlo?

@Binds è un'alternativa efficiente a @Provides quando un metodo accetta esattamente un parametro e restituisce il suo tipo come interfaccia. @Binds genera un cast diretto senza chiamare il metodo, riducendo la quantità di codice generato e migliorando le prestazioni dell'iniezione.

Riepilogo

  • Dagger — framework DI a compile-time con annotazioni @Module, @Provides, @Component e generazione di codice tramite APT
  • Hilt — wrapper Android su Dagger con @HiltAndroidApp, @AndroidEntryPoint, @InstallIn e componenti predefiniti
  • Component gestisce il grafo delle dipendenze, Module fornisce classi di terze parti, Provides fornisce factory di oggetti
  • Scope (@Singleton, @ViewModelScoped, @ActivityScoped) definisce la durata dell'oggetto nel grafo di Dagger
  • @HiltViewModel automatizza la creazione di ViewModel, eliminando le factory manuali ViewModelProvider.Factory
  • @InstallIn isola i moduli per componenti, prevenendo perdite di dipendenze tra i livelli dell'applicazione
  • Hilt è raccomandato da Google per tutti i nuovi progetti Android, Dagger per architetture non Android e personalizzate

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