Dagger — este un framework de injectare a dependențelor pentru Java și Kotlin, care generează cod DI în faza de compilare prin procesarea adnotărilor. Hilt — este un strat peste Dagger pentru Android, care simplifică configurarea componentelor și a ciclului de viață. Conform datelor Google, 2025, Hilt este utilizat în peste 70% din aplicațiile Android din Google Play Top-100, suportând Activity, Fragment, ViewModel și Service prin componente predefinite. Ambele framework-uri asigură verificarea grafului de dependențe la compilare, eliminând erorile de injectare la runtime.
Principalele aspecte
Dagger — este un framework de injectare a dependențelor cu generare de cod la compilare (compile-time DI). Dezvoltat în Square și ulterior transferat Google, Dagger utilizează procesorul de adnotări Java APT pentru a analiza graful dependențelor și a genera clase factory. Spre deosebire de runtime-DI (Guice, Koin), Dagger nu folosește reflexie — tot codul este creat la compilare, asigurând performanță maximă la runtime și detectarea erorilor în faza de build.
Hilt — este o bibliotecă de la Google, construită deasupra Dagger și optimizată pentru Android. Hilt oferă componente predefinite corespunzătoare ciclului de viață al componentelor Android: @SingletonComponent pentru Application, @ActivityComponent pentru Activity, @FragmentComponent pentru Fragment, @ViewModelComponent pentru ViewModel. Aceasta elimină configurarea de rutină a Component și Module, necesară în Dagger pur. Hilt generează, de asemenea, graful dependențelor pentru fiecare componentă Android automat prin @AndroidEntryPoint.
Conform Google I/O 2024, Hilt este soluția recomandată pentru DI în aplicațiile Android scrise în Kotlin. Bibliotecile Jetpack (Navigation, Room, WorkManager) au integrare încorporată cu Hilt prin @HiltViewModel și @HiltWorker. În proiecte care nu folosesc Android (biblioteci Java/Kotlin pure, aplicații server), se aplică Dagger pur fără stratul Hilt.
Fără un framework DI, dezvoltatorul creează obiecte manual prin constructori sau fabrici, transmițând dependențe în lanț. Fiecare cerință nouă — modificarea semnăturii tuturor constructorilor din lanț. Dagger automatizează acest proces: este suficient să declari ce tip este necesar (@Inject constructor), iar Dagger va crea graful dependențelor, rezolvând toate tipurile imbricate. La modificarea dependențelor, Dagger actualizează automat codul generat — este imposibil de greșit în lanț.
Injectarea dependențelor (Dependency Injection) — este un model în care un obiect își primește dependențele din exterior, nu le creează singur. DI implementează principiul inversiunii controlului (IoC): clasa nu este responsabilă pentru crearea propriilor dependențe, ci le declară prin constructor, metodă sau câmp. Injectarea prin constructor (constructor injection) este considerată cea mai preferată, deoarece garantează că obiectul este creat într-o stare validă.
| Tip de injectare | Sintaxa Dagger | Când se utilizează |
|---|---|---|
| Constructor injection | @Inject constructor | Mod principal — pentru toate clasele proprii |
| Field injection | @Inject lateinit var | Doar pentru componente Android (Activity, Fragment) |
| Method injection | @Inject fun bind() | Pentru inițializare post-construct |
Principalele avantaje ale DI includ testabilitatea (dependențele pot fi înlocuite cu obiecte mock), cuplarea slabă (clasele depind de interfețe, nu de implementări) și gestionarea explicită a ciclului de viață al obiectelor prin scope-uri. Dagger garantează automat că un obiect este creat o singură dată în cadrul scope-ului său și este distrus la ieșirea din scope.
Component — elementul central al grafului de dependențe Dagger. Este o interfață adnotată cu @Component care descrie puntea dintre Module și țintele de injectare. Dagger generează implementarea Component (de exemplu, DaggerAppComponent) în faza de compilare. Component determină ce tipuri sunt disponibile pentru injectare, prin metode abstracte care returnează tipurile necesare sau prin metode inject care primesc un obiect pentru field injection.
// Modul: furnizează dependențe pe care Dagger nu le poate crea singur
@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: leagă Module și țintele de injectare
@Component(modules = [NetworkModule::class])
interface AppComponent {
fun inject(activity: MainActivity)
fun getApiService(): ApiService
}
@Module — este o clasă care conține metode cu @Provides ce returnează instanțe ale dependențelor. Module este utilizat pentru tipurile pe care Dagger nu le poate crea automat: biblioteci terțe (OkHttp, Retrofit), obiecte cu parametri de constructor, interfețe cu alegere de implementare. @Binds — alternativă la @Provides pentru cazurile când metoda returnează o interfață și primește o singură implementare: Dagger generează un cast direct fără a apela metoda.
@Scope determină durata de viață a obiectului în graful de dependențe. @Singleton — obiectul este creat o dată pentru întreaga aplicație. @ActivityScoped — obiectul trăiește cât trăiește Activity. @FragmentScoped — cât trăiește Fragment. Fără scope, Dagger creează o nouă instanță la fiecare injectare. @Reusable — scope pentru obiecte care nu trebuie să fie singleton, dar a căror creare este costisitoare — Dagger poate cache-ui instanța, dar nu garantează acest lucru.
Hilt simplifică configurarea Dagger pentru Android datorită componentelor predefinite și generării automate a grafului de bază. Adnotarea @HiltAndroidApp pe clasa Application activează generarea componentei Hilt. Fără această adnotare Hilt nu funcționează — este obligatorie pentru orice aplicație Android care utilizează Hilt. @HiltAndroidApp creează componenta părinte SingletonComponent, de care moștenesc toate celelalte componente ale aplicației.
@HiltAndroidApp
class MyApplication : Application()
@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
@Inject lateinit var apiService: ApiService
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// apiService a fost deja injectat înainte de apelul 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 — adnotare pentru Activity, Fragment, Service, BroadcastReceiver și View. Generează o componentă Hilt pentru fiecare tip: @AndroidEntryPoint pe Activity creează ActivityComponent, care moștenește din SingletonComponent. Componenta copil primește automat toate dependențele părintelui. Field injection cu @Inject lateinit var este disponibilă doar în clasele adnotate cu @AndroidEntryPoint — în clasele obișnuite se utilizează constructor injection.
@InstallIn indică în ce componentă Hilt este instalat modulul. NetworkModule cu @InstallIn(SingletonComponent::class) este disponibil în întreaga aplicație. Module cu @InstallIn(ActivityComponent::class) — doar în Activity. Aceasta izolează grafurile de dependențe: modulele specifice Activity nu sunt vizibile în Fragment și ViewModel, prevenind utilizarea accidentală a dependențelor nepermise. @ApplicationContext — calificatorul încorporat Hilt pentru obținerea contextului aplicației.
Când este necesară injectarea a două implementări diferite ale aceluiași interfață, se utilizează calificatoare. Hilt suportă @Named pentru identificatori text și adnotări personalizate cu @Qualifier. De exemplu, @Named("baseUrl") și @Named("imageBaseUrl") pentru configurații text diferite. Calificatoarele personalizate sunt preferabile față de @Named datorită verificării la compilare — un nume text incorect nu va fi detectat până la runtime.
@HiltViewModel — adnotare care înlocuiește fabrica manuală ViewModelProvider.Factory. Clasa adnotată cu @HiltViewModel cu @Inject constructor primește automat toate dependențele prin Dagger. Hilt generează ViewModelFactory utilizată de Jetpack ViewModelProvider. Fără Hilt, dezvoltatorul ar trebui să scrie fabrica manual, transmițând fiecare parametru din Activity sau 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()
}
}
}
// În Activity — Hilt creează automat ViewModel
@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
private val viewModel: MainViewModel by viewModels()
}
ViewModelScoped — scope Hilt pentru dependențe care trăiesc cât trăiește ViewModel. Dacă două ViewModel de același tip injectează aceeași dependență @ViewModelScoped, fiecare primește propria instanță. Aceasta diferențiază @ViewModelScoped de @ActivityScoped, unde un Activity primește o singură instanță pentru toate fragmentele. Pentru dependențe specifice ViewModel (de exemplu, SavedStateHandle) se utilizează @HiltViewModel cu @Inject constructor(savedStateHandle: SavedStateHandle).
Hilt suportă assisted injection prin biblioteca Hilt Extensions. Assisted injection permite transmiterea parametrilor către constructor în timpul injectării, când o parte din dependențe sunt cunoscute doar la runtime (de exemplu, ID-ul utilizatorului din intent). Pentru assisted injection se utilizează @AssistedInject în combinație cu parametrii @Assisted. Hilt generează AssistedFactory, care poate fi injectată în mod standard.
Dagger pur necesită crearea manuală a Component, definirea scope-urilor și configurarea injectării în fiecare componentă Android. Dezvoltatorul creează AppComponent, ActivityComponent, FragmentComponent, gestionează relațiile lor prin @Subcomponent. Această abordare oferă control maxim, dar necesită o cantitate semnificativă de cod boilerplate. Dagger este utilizat în proiecte mari unde se necesită o arhitectură DI personalizată sau în proiecte non-Android Java/Kotlin.
Hilt automatizează boilerplate: un @HiltAndroidApp, un @AndroidEntryPoint pentru fiecare componentă, scope-uri predefinite. Hilt este recomandat de Google pentru toate proiectele Android noi. Migrarea de la Dagger la Hilt include înlocuirea Component cu @InstallIn, înlocuirea @Subcomponent cu componente Hilt predefinite și înlocuirea ViewModelProvider.Factory manual cu @HiltViewModel. Majoritatea claselor @Module se transferă cu adăugarea @InstallIn fără modificarea metodelor @Provides.
| Caracteristică | Dagger | Hilt |
|---|---|---|
| Configurare | Manuală: Component, Subcomponent, Builder | Automată: @HiltAndroidApp, @AndroidEntryPoint |
| Componente Android | Niciuna predefinită | 12+ componente încorporate |
| ViewModel | Fabrică manuală | @HiltViewModel + @Inject constructor |
| Multimodul | Prin @Component(dependencies) | Prin @InstallIn + agregare |
| Complexitate | Ridicată — necesită experiență | Scăzută — intuitivă |
| Flexibilitate | Maximă | Standard (acoperă 95% din scenarii) |
Limitări Hilt: biblioteca suportă doar Android (nu este potrivită pentru proiecte Java pur server), impune o anumită structură de componente (dificil de suprascris) și adaugă dependența de android.hilt:hilt-navigation-compose pentru proiectele Jetpack Compose. Pentru aplicațiile Compose, Hilt oferă @HiltViewModel disponibil în Composable prin hiltViewModel() — fără a furniza manual ViewModel din Activity.
Întrebări frecvente
Dagger — framework de bază DI la compilare cu configurare manuală a Component și Module. Hilt — strat pentru Android care automatizează crearea componentelor și integrarea cu ciclul de viață al Activity, Fragment, ViewModel, Service și BroadcastReceiver.
@HiltAndroidApp activează generarea componentei Hilt pentru Application. Fără această adnotare, Hilt nu poate crea componenta de bază SingletonComponent, de care moștenesc toate ActivityComponent, FragmentComponent și ViewModelComponent. Adnotarea este obligatorie pentru orice proiect Hilt.
Hilt Navigation oferă @HiltViewModel pentru ViewModel în NavBackStackEntry și hiltNavGraphViewModels() pentru scoparea ViewModel în cadrul grafului de navigare. Biblioteca android.hilt:hilt-navigation-fragment creează automat ViewModel pentru fiecare NavBackStackEntry.
Utilizați @ApplicationContext pentru contextul aplicației sau @ActivityContext pentru contextul Activity. Hilt oferă aceste calificatoare încorporate în biblioteca android.hilt:hilt-android. @ActivityContext este disponibil doar în modulele instalate în ActivityComponent.
@Binds — o alternativă eficientă la @Provides, când metoda primește exact un parametru și returnează tipul său ca interfață. @Binds generează un cast direct fără a apela metoda, ceea ce reduce cantitatea de cod generat și crește performanța la injectare.
Rezumat
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.
Citiți și