Dagger — ay isang framework ng dependency injection para sa Java at Kotlin na bumubuo ng DI code sa yugto ng compilation sa pamamagitan ng pagproseso ng anotasyon. Hilt — ay isang layer sa ibabaw ng Dagger para sa Android na nagpapasimple sa configuration ng mga component at lifecycle. Ayon sa datos ng Google, 2025, ang Hilt ay ginagamit sa higit 70% ng mga Android app mula sa Google Play Top-100, sumusuporta sa Activity, Fragment, ViewModel at Service sa pamamagitan ng mga paunang natukoy na component. Ang parehong framework ay nagbibigay ng pagsusuri ng dependency graph sa oras ng compilation, inaalis ang mga error sa injection sa runtime.
Mga pangunahing punto
Dagger — ay isang dependency injection framework na may code generation sa oras ng compilation (compile-time DI). Binuo sa Square at kalaunan ay inilipat sa Google, ang Dagger ay gumagamit ng Java APT annotation processor para suriin ang dependency graph at bumuo ng factory class. Hindi tulad ng runtime-DI (Guice, Koin), hindi gumagamit ang Dagger ng reflection — lahat ng code ay ginagawa sa compilation, na nagbibigay ng maximum na performance sa runtime at pagtuklas ng error sa yugto ng build.
Hilt — ay isang library mula sa Google, na binuo sa ibabaw ng Dagger at na-optimize para sa Android. Ang Hilt ay nagbibigay ng mga paunang natukoy na component na tumutugma sa lifecycle ng Android component: @SingletonComponent para sa Application, @ActivityComponent para sa Activity, @FragmentComponent para sa Fragment, @ViewModelComponent para sa ViewModel. Inaalis nito ang routine na configuration ng Component at Module na kinakailangan sa purong Dagger. Ang Hilt ay bumubuo rin ng dependency graph para sa bawat Android component nang awtomatiko sa pamamagitan ng @AndroidEntryPoint.
Ayon sa Google I/O 2024, ang Hilt ay inirerekomendang solusyon sa DI para sa mga Android app na nakasulat sa Kotlin. Ang mga Jetpack library (Navigation, Room, WorkManager) ay may built-in na integrasyon sa Hilt sa pamamagitan ng @HiltViewModel at @HiltWorker. Sa mga project na hindi gumagamit ng Android (purong Java/Kotlin library, server app), ang purong Dagger ay ginagamit nang walang Hilt layer.
Kung walang DI framework, ang developer ay gumagawa ng object nang manu-mano sa pamamagitan ng constructor o factory, na nagpapasa ng dependencies sa kahabaan ng chain. Bawat bagong kinakailangan — pagbabago ng lagda ng lahat ng constructor sa chain. Dagger ay awtomatiko ang prosesong ito: sapat na upang ideklara kung anong uri ang kinakailangan (@Inject constructor), at gagawa ang Dagger ng dependency graph, na nilulutas ang lahat ng nested na uri. Kapag nagbago ang dependencies, awtomatikong ina-update ng Dagger ang nabuong code — imposibleng magkamali sa chain.
Dependency injection — ay isang pattern kung saan ang isang object ay tumatanggap ng mga dependencies nito mula sa labas, hindi gumagawa ng mga ito mismo. Ang DI ay nagpapatupad ng prinsipyo ng inversion of control (IoC): ang klase ay hindi responsable para sa paggawa ng sarili nitong dependencies, kundi idineklara ang mga ito sa pamamagitan ng constructor, method, o field. Ang injection sa pamamagitan ng constructor (constructor injection) ay itinuturing na pinakagusto dahil ginagarantiyahan nito na ang object ay ginagawa sa isang wastong estado.
| Uri ng injection | Sintaks ng Dagger | Kailan gagamitin |
|---|---|---|
| Constructor injection | @Inject constructor | Pangunahing paraan — para sa lahat ng sariling klase |
| Field injection | @Inject lateinit var | Para lamang sa Android component (Activity, Fragment) |
| Method injection | @Inject fun bind() | Para sa post-construct initialization |
Ang mga pangunahing bentahe ng DI ay kinabibilangan ng testability (ang dependencies ay maaaring palitan ng mock object), maluwag na pagkakaugnay (ang mga klase ay nakadepende sa interface, hindi sa implementasyon), at malinaw na pamamahala ng lifecycle ng object sa pamamagitan ng scope. Dagger ay awtomatikong ginagarantiyahan na ang isang object ay ginagawa nang isang beses sa loob ng scope nito at sinisira kapag umalis sa scope.
Component — ang pangunahing elemento ng Dagger dependency graph. Ito ay isang interface na may anotasyong @Component na naglalarawan ng tulay sa pagitan ng Module at mga target ng injection. Ang Dagger ay bumubuo ng implementasyon ng Component (hal., DaggerAppComponent) sa yugto ng compilation. Tinutukoy ng Component kung aling mga uri ang available para sa injection, sa pamamagitan ng abstract method na nagbabalik ng mga kinakailangang uri o sa pamamagitan ng inject method na tumatanggap ng object para sa field injection.
// Module: nagbibigay ng dependencies na hindi kayang gawin ng Dagger nang mag-isa
@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: nag-uugnay ng Module at injection target
@Component(modules = [NetworkModule::class])
interface AppComponent {
fun inject(activity: MainActivity)
fun getApiService(): ApiService
}
@Module — ay isang klase na naglalaman ng mga method na may @Provides na nagbabalik ng instance ng dependencies. Ang Module ay ginagamit para sa mga uri na hindi awtomatikong magawa ng Dagger: mga library ng third-party (OkHttp, Retrofit), mga object na may parameter ng constructor, mga interface na may pagpili ng implementasyon. @Binds — alternatibo sa @Provides sa mga kaso kung saan ang method ay nagbabalik ng interface at tumatanggap ng iisang implementasyon: ang Dagger ay bumubuo ng direktang cast nang hindi tinatawag ang method.
@Scope ay tumutukoy sa buhay ng object sa dependency graph. @Singleton — ang object ay ginagawa nang isang beses para sa buong app. @ActivityScoped — ang object ay nabubuhay hangga't nabubuhay ang Activity. @FragmentScoped — hangga't nabubuhay ang Fragment. Kung walang scope, ang Dagger ay gumagawa ng bagong instance sa bawat injection. @Reusable — scope para sa mga object na hindi kailangang maging singleton ngunit ang paggawa ay mahal — maaaring i-cache ng Dagger ang instance, ngunit hindi ito ginagarantiyahan.
Hilt ay nagpapasimple ng configuration ng Dagger para sa Android dahil sa mga paunang natukoy na component at awtomatikong pagbuo ng base graph. Ang anotasyong @HiltAndroidApp sa klase ng Application ay nagpapagana ng pagbuo ng Hilt component. Kung wala ang anotasyong ito, hindi gagana ang Hilt — ito ay sapilitan para sa bawat Android app na gumagamit ng Hilt. Ang @HiltAndroidApp ay gumagawa ng parent component na SingletonComponent, na minamana ng lahat ng iba pang component ng app.
@HiltAndroidApp
class MyApplication : Application()
@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
@Inject lateinit var apiService: ApiService
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// Ang apiService ay na-inject na bago ang tawag sa 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 — anotasyon para sa Activity, Fragment, Service, BroadcastReceiver at View. Bumubuo ng Hilt component para sa bawat uri: ang @AndroidEntryPoint sa Activity ay gumagawa ng ActivityComponent na nagmamana mula sa SingletonComponent. Ang child component ay awtomatikong tumatanggap ng lahat ng dependency ng parent. Ang field injection na may @Inject lateinit var ay available lamang sa mga klase na may anotasyong @AndroidEntryPoint — sa mga ordinaryong klase, ginagamit ang constructor injection.
@InstallIn ay nagpapahiwatig kung saang Hilt component naka-install ang module. Ang NetworkModule na may @InstallIn(SingletonComponent::class) ay available sa buong app. Ang Module na may @InstallIn(ActivityComponent::class) — sa Activity lamang. Ito ay naghihiwalay ng dependency graph: ang mga module na partikular sa Activity ay hindi nakikita sa Fragment at ViewModel, na pumipigil sa aksidenteng paggamit ng hindi pinapayagang dependencies. @ApplicationContext — ang built-in na kwalipikador ng Hilt para makuha ang konteksto ng app.
Kapag kinakailangan na mag-inject ng dalawang magkaibang implementasyon ng parehong interface, ginagamit ang mga kwalipikador. Sinusuportahan ng Hilt ang @Named para sa mga text identifier at custom na anotasyon na may @Qualifier. Halimbawa, @Named("baseUrl") at @Named("imageBaseUrl") para sa magkaibang text configuration. Ang custom na kwalipikador ay mas gusto kaysa @Named dahil sa pagsusuri sa yugto ng compilation — ang maling pangalan ng text ay hindi matutuklasan hanggang runtime.
@HiltViewModel — anotasyon na pumapalit sa manual na ViewModelProvider.Factory factory. Ang klase na may anotasyong @HiltViewModel na may @Inject constructor ay awtomatikong tumatanggap ng lahat ng dependencies sa pamamagitan ng Dagger. Ang Hilt ay bumubuo ng ViewModelFactory na ginagamit ng Jetpack ViewModelProvider. Kung wala ang Hilt, ang developer ay kailangang sumulat ng factory nang manu-mano, na nagpapasa ng bawat parameter mula sa Activity o 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()
}
}
}
// Sa Activity — awtomatikong gumagawa ng ViewModel ang Hilt
@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
private val viewModel: MainViewModel by viewModels()
}
ViewModelScoped — Hilt scope para sa mga dependency na nabubuhay hangga't nabubuhay ang ViewModel. Kung dalawang ViewModel ng parehong uri ang nag-inject ng parehong @ViewModelScoped dependency, bawat isa ay nakakakuha ng sarili nitong instance. Ito ay nag-iiba ng @ViewModelScoped mula sa @ActivityScoped, kung saan ang isang Activity ay nakakakuha ng isang instance para sa lahat ng fragment. Para sa mga dependency na partikular sa ViewModel (hal., SavedStateHandle) gamitin ang @HiltViewModel na may @Inject constructor(savedStateHandle: SavedStateHandle).
Sinusuportahan ng Hilt ang assisted injection sa pamamagitan ng Hilt Extensions library. Ang assisted injection ay nagpapahintulot sa pagpasa ng mga parameter sa constructor sa panahon ng injection, kapag ang bahagi ng dependencies ay alam lamang sa runtime (hal., ID ng user mula sa intent). Para sa assisted injection, gamitin ang @AssistedInject kasama ng @Assisted na parameter. Ang Hilt ay bumubuo ng AssistedFactory na maaaring i-inject sa karaniwang paraan.
Purong Dagger ay nangangailangan ng manual na paggawa ng Component, pagtukoy ng scope, at configuration ng injection sa bawat Android component. Ang developer ay gumagawa ng AppComponent, ActivityComponent, FragmentComponent, namamahala ng kanilang relasyon sa pamamagitan ng @Subcomponent. Ang pamamaraang ito ay nagbibigay ng maximum na kontrol ngunit nangangailangan ng malaking halaga ng boilerplate code. Ang Dagger ay ginagamit sa malalaking project kung saan kinakailangan ang custom na DI architecture o sa mga non-Android Java/Kotlin project.
Hilt ay awtomatiko ang boilerplate: isang @HiltAndroidApp, isang @AndroidEntryPoint para sa bawat component, mga paunang natukoy na scope. Ang Hilt ay inirerekomenda ng Google para sa lahat ng bagong Android project. Ang migrasyon mula Dagger patungong Hilt ay kinabibilangan ng pagpapalit ng Component ng @InstallIn, pagpapalit ng @Subcomponent ng mga paunang natukoy na Hilt component, at pagpapalit ng manual na ViewModelProvider.Factory ng @HiltViewModel. Karamihan sa mga @Module na klase ay inililipat sa pagdaragdag ng @InstallIn nang hindi binabago ang @Provides method.
| Katangian | Dagger | Hilt |
|---|---|---|
| Configuration | Manual: Component, Subcomponent, Builder | Awtomatiko: @HiltAndroidApp, @AndroidEntryPoint |
| Android component | Walang paunang natukoy | 12+ built-in na component |
| ViewModel | Manual na factory | @HiltViewModel + @Inject constructor |
| Multi-module | Sa pamamagitan ng @Component(dependencies) | Sa pamamagitan ng @InstallIn + agregasyon |
| Kompleksidad | Mataas — nangangailangan ng karanasan | Mababa — intuitive |
| Flexibility | Maximum | Standard (sumasaklaw sa 95% ng scenario) |
Mga limitasyon ng Hilt: ang library ay sumusuporta lamang sa Android (hindi angkop para sa purong server Java project), nagpapataw ng tiyak na istraktura ng component (mahirap i-override), at nagdaragdag ng dependency sa android.hilt:hilt-navigation-compose para sa Jetpack Compose project. Para sa Compose app, ang Hilt ay nagbibigay ng @HiltViewModel na available sa Composable sa pamamagitan ng hiltViewModel() — nang walang manual na pagbibigay ng ViewModel mula sa Activity.
Mga madalas itanong
Dagger — pangunahing compile-time DI framework na may manual na configuration ng Component at Module. Hilt — Android layer na nag-automate ng paggawa ng component at integrasyon sa lifecycle ng Activity, Fragment, ViewModel, Service at BroadcastReceiver.
@HiltAndroidApp ay nagpapagana ng pagbuo ng Hilt component para sa Application. Kung wala ang anotasyong ito, hindi magagawa ng Hilt ang base component na SingletonComponent, na minamana ng lahat ng ActivityComponent, FragmentComponent at ViewModelComponent. Ang anotasyon ay sapilitan para sa bawat Hilt project.
Hilt Navigation ay nagbibigay ng @HiltViewModel para sa ViewModel sa NavBackStackEntry at hiltNavGraphViewModels() para sa scoping ng ViewModel sa loob ng navigation graph. Ang library na android.hilt:hilt-navigation-fragment ay awtomatikong gumagawa ng ViewModel para sa bawat NavBackStackEntry.
Gamitin ang @ApplicationContext para sa konteksto ng app o @ActivityContext para sa konteksto ng Activity. Ang Hilt ay nagbibigay ng mga kwalipikador na ito na built-in sa library na android.hilt:hilt-android. Ang @ActivityContext ay available lamang sa mga module na naka-install sa ActivityComponent.
@Binds — mahusay na alternatibo sa @Provides, kapag ang method ay tumatanggap ng eksaktong isang parameter at nagbabalik ng uri nito bilang interface. Ang @Binds ay bumubuo ng direktang cast nang hindi tinatawag ang method, na nagbabawas ng dami ng nabuong code at nagpapataas ng performance sa panahon ng injection.
Buod
Gagawa kami ng mobile application na turnkey
Gumagawa ang IT Sectr ng mga iOS at Android application para sa mga startup at negosyo mula noong 2017. Magpapayo kami sa iyo at magmumungkahi ng pinakamahusay na solusyon.
Basahin din