Dagger / Hilt: co to jest, DI i zastosowanie

Autor: IT Sectr Opublikowano: 2026-05-03 Czas czytania: 9 min

Dagger — to framework wstrzykiwania zależności dla Javy i Kotlina, generujący kod DI na etapie kompilacji poprzez przetwarzanie adnotacji. Hilt — to nakładka na Dagger dla Androida, upraszczająca konfigurację komponentów i cyklu życia. Według danych Google, 2025, Hilt jest używany w ponad 70% aplikacji Android z Google Play Top-100, obsługując Activity, Fragment, ViewModel i Service poprzez predefiniowane komponenty. Oba frameworki zapewniają sprawdzanie grafu zależności w czasie kompilacji, eliminując błędy wstrzykiwania w czasie wykonania.

Najważniejsze

  • Dagger — framework DI czasu kompilacji z generowaniem kodu przez adnotacje @Module, @Provides, @Component
  • Hilt — nakładka na Androida, upraszczająca Dagger przez @HiltAndroidApp, @AndroidEntryPoint, @HiltViewModel
  • Component — graf zależności, łączący Module z celami Inject poprzez metody proxy
  • Scope — @Singleton, @ViewModelScoped, @ActivityScoped zarządzają czasem życia wstrzykiwanych obiektów
  • Hilt obsługuje projekty wielomodułowe poprzez @InstallIn dla izolowanych grafów zależności

Czym jest Dagger / Hilt?

Dagger — to framework wstrzykiwania zależności z generowaniem kodu na etapie kompilacji (compile-time DI). Opracowany w Square, a później przekazany Google, Dagger używa procesora adnotacji Java APT do analizy grafu zależności i generowania klas fabrycznych. W przeciwieństwie do runtime-DI (Guice, Koin), Dagger nie używa refleksji — cały kod jest tworzony podczas kompilacji, co zapewnia maksymalną wydajność w czasie wykonania i wykrywanie błędów na etapie budowania.

Hilt — to biblioteka od Google, zbudowana na bazie Dagger i zoptymalizowana pod kątem Androida. Hilt dostarcza predefiniowane komponenty odpowiadające cyklowi życia komponentów Androida: @SingletonComponent dla Application, @ActivityComponent dla Activity, @FragmentComponent dla Fragment, @ViewModelComponent dla ViewModel. Eliminuje to rutynową konfigurację Component i Module, wymaganą w czystym Dagger. Hilt generuje również graf zależności dla każdego komponentu Androida automatycznie poprzez @AndroidEntryPoint.

Według danych Google I/O 2024, Hilt jest rekomendowanym rozwiązaniem DI w aplikacjach Android napisanych w Kotlinie. Biblioteki Jetpack (Navigation, Room, WorkManager) mają wbudowaną integrację z Hilt poprzez @HiltViewModel i @HiltWorker. W projektach niekorzystających z Androida (czyste biblioteki Java/Kotlin, aplikacje serwerowe) stosuje się czysty Dagger bez nakładki Hilt.

Problem ręcznego wstrzykiwania zależności

Bez frameworka DI programista tworzy obiekty ręcznie poprzez konstruktory lub fabryki, przekazując zależności w łańcuchu. Każde nowe wymaganie — zmiana sygnatury wszystkich konstruktorów w łańcuchu. Dagger automatyzuje ten proces: wystarczy zadeklarować, jaki typ jest wymagany (@Inject constructor), a Dagger utworzy graf zależności, rozwiązując wszystkie zagnieżdżone typy. Przy zmianie zależności Dagger automatycznie aktualizuje wygenerowany kod — nie można popełnić błędu w łańcuchu.

Zasady wstrzykiwania zależności

Wstrzykiwanie zależności (Dependency Injection) — to wzorzec, w którym obiekt otrzymuje swoje zależności z zewnątrz, a nie tworzy je samodzielnie. DI realizuje zasadę odwrócenia sterowania (IoC): klasa nie odpowiada za tworzenie swoich zależności, ale deklaruje je poprzez konstruktor, metodę lub pole. Wstrzykiwanie przez konstruktor (constructor injection) jest uważane za najbardziej preferowane, ponieważ gwarantuje, że obiekt jest tworzony w prawidłowym stanie.

Typ wstrzykiwaniaSkładnia DaggerKiedy używać
Constructor injection@Inject constructorGłówny sposób — dla wszystkich własnych klas
Field injection@Inject lateinit varTylko dla komponentów Androida (Activity, Fragment)
Method injection@Inject fun bind()Do inicjalizacji post-construct

Zalety compile-time DI

Główne zalety DI obejmują testowalność (zależności można zastępować przez obiekty mock), słabe powiązanie (klasy zależą od interfejsów, a nie implementacji) oraz jawne zarządzanie cyklem życia obiektów poprzez scopes. Dagger automatycznie gwarantuje, że obiekt jest tworzony raz w ramach swojego scope'u i niszczony po wyjściu z scope'u.

Architektura Dagger: Component, Module, Provides

Component — centralny element grafu zależności Dagger. To interfejs adnotowany @Component, który opisuje most między Module a celami wstrzykiwania. Dagger generuje implementację Component (np. DaggerAppComponent) na etapie kompilacji. Component określa, które typy są dostępne do wstrzykiwania, poprzez abstrakcyjne metody zwracające wymagane typy lub metody inject przyjmujące obiekt do field injection.

kotlin
// Moduł: dostarcza zależności, których Dagger nie może sam utworzyć
@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)
    }
}

// Komponent: łączy Module i cele wstrzykiwania
@Component(modules = [NetworkModule::class])
interface AppComponent {
    fun inject(activity: MainActivity)
    fun getApiService(): ApiService
}

@Module — to klasa zawierająca metody z @Provides, które zwracają instancje zależności. Module jest używany dla typów, których Dagger nie może utworzyć automatycznie: biblioteki zewnętrzne (OkHttp, Retrofit), obiekty z parametrami konstruktora, interfejsy z wyborem implementacji. @Binds — alternatywa dla @Provides w przypadkach, gdy metoda zwraca interfejs i przyjmuje pojedynczą implementację: Dagger generuje bezpośrednie rzutowanie bez wywoływania metody.

@Scope określa czas życia obiektu w grafie zależności. @Singleton — obiekt jest tworzony raz dla całej aplikacji. @ActivityScoped — obiekt żyje, dopóki żyje Activity. @FragmentScoped — dopóki żyje Fragment. Bez scope'u Dagger tworzy nową instancję przy każdym wstrzyknięciu. @Reusable — scope dla obiektów, które nie muszą być singletonami, ale ich tworzenie jest kosztowne — Dagger może buforować instancję, ale nie gwarantuje tego.

Hilt dla Androida: @HiltAndroidApp i @AndroidEntryPoint

Hilt upraszcza konfigurację Dagger dla Androida dzięki predefiniowanym komponentom i automatycznemu generowaniu podstawowego grafu. Adnotacja @HiltAndroidApp na klasie Application uruchamia generowanie komponentu Hilt. Bez tej adnotacji Hilt nie działa — jest obowiązkowa dla każdej aplikacji Android używającej Hilt. @HiltAndroidApp tworzy nadrzędny komponent SingletonComponent, od którego dziedziczą wszystkie pozostałe komponenty aplikacji.

kotlin
@HiltAndroidApp
class MyApplication : Application()

@AndroidEntryPoint
class MainActivity : AppCompatActivity() {

    @Inject lateinit var apiService: ApiService

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        // apiService został już wstrzyknięty przed wywołaniem 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 — adnotacja dla Activity, Fragment, Service, BroadcastReceiver i View. Generuje komponent Hilt dla każdego typu: @AndroidEntryPoint na Activity tworzy ActivityComponent, który dziedziczy z SingletonComponent. Komponent potomny automatycznie otrzymuje wszystkie zależności rodzica. Field injection z @Inject lateinit var jest dostępny tylko w klasach adnotowanych @AndroidEntryPoint — w zwykłych klasach używa się constructor injection.

@InstallIn wskazuje, do którego komponentu Hilt instalowany jest moduł. NetworkModule z @InstallIn(SingletonComponent::class) jest dostępny w całej aplikacji. Module z @InstallIn(ActivityComponent::class) — tylko w Activity. Izoluje to grafy zależności: moduły specyficzne dla Activity nie są widoczne w Fragment i ViewModel, zapobiegając przypadkowemu użyciu niedozwolonych zależności. @ApplicationContext — wbudowany kwalifikator Hilt do uzyskania kontekstu aplikacji.

Qualifier: @Named i niestandardowe kwalifikatory

Gdy wymagane jest wstrzyknięcie dwóch różnych implementacji tego samego interfejsu, używa się kwalifikatorów. Hilt obsługuje @Named dla identyfikatorów tekstowych oraz niestandardowe adnotacje z @Qualifier. Na przykład @Named("baseUrl") i @Named("imageBaseUrl") dla różnych konfiguracji tekstowych. Niestandardowe kwalifikatory są preferowane nad @Named ze względu na sprawdzanie na etapie kompilacji — nieprawidłowa nazwa tekstu nie zostanie wykryta do czasu wykonania.

Hilt ViewModel: @HiltViewModel i @Inject constructor

@HiltViewModel — adnotacja zastępująca ręczną fabrykę ViewModelProvider.Factory. Klasa adnotowana @HiltViewModel z @Inject constructor automatycznie otrzymuje wszystkie zależności poprzez Dagger. Hilt generuje ViewModelFactory używaną przez Jetpack ViewModelProvider. Bez Hilt programista musiałby pisać fabrykę ręcznie, przekazując każdy parametr z Activity lub fragmentu.

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()
        }
    }
}

// W Activity — Hilt automatycznie tworzy ViewModel
@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
    private val viewModel: MainViewModel by viewModels()
}

ViewModelScoped — scope Hilt dla zależności, które żyją, dopóki żyje ViewModel. Jeśli dwa ViewModel tego samego typu wstrzykują tę samą zależność @ViewModelScoped, każdy otrzymuje własną instancję. To odróżnia @ViewModelScoped od @ActivityScoped, gdzie jedno Activity otrzymuje jedną instancję dla wszystkich fragmentów. Dla zależności specyficznych dla ViewModel (np. SavedStateHandle) używa się @HiltViewModel z @Inject constructor(savedStateHandle: SavedStateHandle).

Hilt obsługuje assisted injection poprzez bibliotekę Hilt Extensions. Assisted injection pozwala przekazywać parametry do konstruktora podczas wstrzykiwania, gdy część zależności jest znana tylko w czasie wykonania (np. ID użytkownika z intencji). Do assisted injection używa się @AssistedInject w kombinacji z parametrami @Assisted. Hilt generuje AssistedFactory, którą można wstrzyknąć w standardowy sposób.

Dagger vs Hilt: porównanie i migracja

Czysty Dagger wymaga ręcznego tworzenia Component, definiowania scope'ów i konfiguracji wstrzykiwania w każdym komponencie Androida. Programista tworzy AppComponent, ActivityComponent, FragmentComponent, zarządza ich relacjami poprzez @Subcomponent. To podejście daje maksymalną kontrolę, ale wymaga znacznej ilości kodu boilerplate. Dagger jest używany w dużych projektach, gdzie wymagana jest niestandardowa architektura DI, lub w nie-Androidowych projektach Java/Kotlin.

Hilt automatyzuje boilerplate: jeden @HiltAndroidApp, jeden @AndroidEntryPoint dla każdego komponentu, predefiniowane scope'y. Hilt jest rekomendowany przez Google dla wszystkich nowych projektów Android. Migracja z Dagger na Hilt obejmuje zastąpienie Component przez @InstallIn, zastąpienie @Subcomponent przez predefiniowane komponenty Hilt i zastąpienie ręcznego ViewModelProvider.Factory przez @HiltViewModel. Większość klas @Module jest przenoszona z dodaniem @InstallIn bez zmiany metod @Provides.

CechaDaggerHilt
KonfiguracjaRęczna: Component, Subcomponent, BuilderAuto: @HiltAndroidApp, @AndroidEntryPoint
Komponenty AndroidBrak predefiniowanych12+ wbudowanych komponentów
ViewModelRęczna fabryka@HiltViewModel + @Inject constructor
MultimodulePrzez @Component(dependencies)Przez @InstallIn + agregację
ZłożonośćWysoka — wymaga doświadczeniaNiska — intuicyjna
ElastycznośćMaksymalnaStandardowa (pokrywa 95% scenariuszy)

Ograniczenia Hilt: biblioteka obsługuje tylko Android (nie nadaje się dla czysto serwerowych projektów Java), narzuca określoną strukturę komponentów (trudno nadpisać) i dodaje zależność od android.hilt:hilt-navigation-compose dla projektów Jetpack Compose. Dla aplikacji Compose Hilt udostępnia @HiltViewModel dostępny w Composable przez hiltViewModel() — bez ręcznego dostarczania ViewModel z Activity.

Często zadawane pytania

Jaka jest różnica między Dagger a Hilt?

Dagger — podstawowy framework DI czasu kompilacji z ręczną konfiguracją Component i Module. Hilt — nakładka na Androida, automatyzująca tworzenie komponentów i integrację z cyklem życia Activity, Fragment, ViewModel, Service i BroadcastReceiver.

Do czego służy @HiltAndroidApp?

@HiltAndroidApp włącza generowanie komponentu Hilt dla Application. Bez tej adnotacji Hilt nie może utworzyć podstawowego komponentu SingletonComponent, od którego dziedziczą wszystkie ActivityComponent, FragmentComponent i ViewModelComponent. Adnotacja jest obowiązkowa dla każdego projektu Hilt.

Jak Hilt współpracuje z Jetpack Navigation?

Hilt Navigation udostępnia @HiltViewModel dla ViewModel w NavBackStackEntry oraz hiltNavGraphViewModels() do scopowania ViewModel w ramach grafu nawigacyjnego. Biblioteka android.hilt:hilt-navigation-fragment automatycznie tworzy ViewModel dla każdego NavBackStackEntry.

Jak wstrzyknąć Context w Hilt?

Użyj @ApplicationContext dla kontekstu aplikacji lub @ActivityContext dla kontekstu Activity. Hilt udostępnia te kwalifikatory wbudowane w bibliotekę android.hilt:hilt-android. @ActivityContext jest dostępny tylko w modułach zainstalowanych w ActivityComponent.

Czym jest @Binds i kiedy go używać?

@Binds — efektywna alternatywa dla @Provides, gdy metoda przyjmuje dokładnie jeden parametr i zwraca jego typ jako interfejs. @Binds generuje bezpośrednie rzutowanie bez wywoływania metody, co zmniejsza ilość wygenerowanego kodu i zwiększa wydajność podczas wstrzykiwania.

Podsumowanie

  • Dagger — framework DI czasu kompilacji z adnotacjami @Module, @Provides, @Component i generowaniem kodu przez APT
  • Hilt — nakładka na Androida na Dagger z @HiltAndroidApp, @AndroidEntryPoint, @InstallIn i predefiniowanymi komponentami
  • Component zarządza grafem zależności, Module dostarcza klasy zewnętrzne, Provides — fabryki obiektów
  • Scope (@Singleton, @ViewModelScoped, @ActivityScoped) określa czas życia obiektu w grafie Dagger
  • @HiltViewModel automatyzuje tworzenie ViewModel, eliminując ręczne fabryki ViewModelProvider.Factory
  • @InstallIn izoluje moduły według komponentów, zapobiegając wyciekowi zależności między warstwami aplikacji
  • Hilt jest rekomendowany przez Google dla wszystkich nowych projektów Android, Dagger — dla nie-Android i niestandardowych architektur DI

Opracujemy aplikację mobilną pod klucz

IT Sectr tworzy aplikacje na iOS i Androida dla startupów i firm od 2017 roku. Doradzimy Ci i zaproponujemy najlepsze rozwiązanie.

Omów projekt

Przeczytaj również