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 è 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.
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.
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 iniezione | Sintassi Dagger | Quando usare |
|---|---|---|
| Constructor injection | @Inject constructor | Metodo principale — per tutte le classi personalizzate |
| Field injection | @Inject lateinit var | Solo per componenti Android (Activity, Fragment) |
| Method injection | @Inject fun bind() | Per inizializzazione post-costruzione |
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.
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.
// 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 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.
@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.
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.
@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.
@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 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.
| Caratteristica | Dagger | Hilt |
|---|---|---|
| Configurazione | Manuale: Component, Subcomponent, Builder | Automatica: @HiltAndroidApp, @AndroidEntryPoint |
| Componenti Android | Nessun predefinito | 12+ componenti integrati |
| ViewModel | Factory manuale | @HiltViewModel + @Inject constructor |
| Multimodulo | Tramite @Component(dependencies) | Tramite @InstallIn + aggregazione |
| Complessità | Alta — esperienza necessaria | Bassa — intuitivamente comprensibile |
| Flessibilità | Massima | Standard (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
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.
@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.
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.
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.
@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
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.
Leggi anche