Dagger — είναι ένα framework ενσωμάτωσης εξαρτήσεων για Java και Kotlin που παράγει κώδικα DI στο στάδιο μεταγλώττισης μέσω επεξεργασίας annotations. Hilt — είναι ένα επίπεδο πάνω από το Dagger για Android που απλοποιεί τη ρύθμιση παραμέτρων των components και του κύκλου ζωής. Σύμφωνα με στοιχεία Google, 2025, το Hilt χρησιμοποιείται σε περισσότερο από το 70% των Android εφαρμογών από το Google Play Top-100, υποστηρίζοντας Activity, Fragment, ViewModel και Service μέσω προκαθορισμένων components. Και τα δύο frameworks παρέχουν έλεγχο του γράφου εξαρτήσεων κατά τη μεταγλώττιση, εξαλείφοντας σφάλματα ενσωμάτωσης κατά το runtime.
Κύρια σημεία
Dagger — είναι ένα framework ενσωμάτωσης εξαρτήσεων με παραγωγή κώδικα κατά τη μεταγλώττιση (compile-time DI). Αναπτύχθηκε στο Square και αργότερα μεταφέρθηκε στην Google, το Dagger χρησιμοποιεί τον επεξεργαστή annotations Java APT για να αναλύσει τον γράφο εξαρτήσεων και να παράγει factory κλάσεις. Σε αντίθεση με το runtime-DI (Guice, Koin), το Dagger δεν χρησιμοποιεί αντανάκλαση (reflection) — όλος ο κώδικας δημιουργείται κατά τη μεταγλώττιση, παρέχοντας μέγιστη απόδοση στο runtime και ανίχνευση σφαλμάτων στο στάδιο build.
Hilt — είναι μια βιβλιοθήκη από την Google, χτισμένη πάνω στο Dagger και βελτιστοποιημένη για Android. Το Hilt παρέχει προκαθορισμένα components που αντιστοιχούν στον κύκλο ζωής των Android components: @SingletonComponent για Application, @ActivityComponent για Activity, @FragmentComponent για Fragment, @ViewModelComponent για ViewModel. Αυτό εξαλείφει τη συνήθη ρύθμιση των Component και Module που απαιτείται στο καθαρό Dagger. Το Hilt επίσης παράγει αυτόματα τον γράφο εξαρτήσεων για κάθε Android component μέσω του @AndroidEntryPoint.
Σύμφωνα με το Google I/O 2024, το Hilt είναι η συνιστώμενη λύση DI για Android εφαρμογές γραμμένες σε Kotlin. Οι βιβλιοθήκες Jetpack (Navigation, Room, WorkManager) έχουν ενσωματωμένη ενοποίηση με το Hilt μέσω @HiltViewModel και @HiltWorker. Σε projects που δεν χρησιμοποιούν Android (καθαρές βιβλιοθήκες Java/Kotlin, εφαρμογές διακομιστή), εφαρμόζεται καθαρό Dagger χωρίς το επίπεδο Hilt.
Χωρίς DI framework, ο προγραμματιστής δημιουργεί αντικείμενα χειροκίνητα μέσω κατασκευαστών ή εργοστασίων, μεταβιβάζοντας εξαρτήσεις κατά μήκος της αλυσίδας. Κάθε νέα απαίτηση — αλλαγή της υπογραφής όλων των κατασκευαστών στην αλυσίδα. Dagger αυτοματοποιεί αυτή τη διαδικασία: αρκεί να δηλώσετε ποιος τύπος απαιτείται (@Inject constructor), και το Dagger θα δημιουργήσει τον γράφο εξαρτήσεων, επιλύοντας όλους τους ένθετους τύπους. Όταν αλλάζουν οι εξαρτήσεις, το Dagger ενημερώνει αυτόματα τον παραγόμενο κώδικα — είναι αδύνατο να γίνει λάθος στην αλυσίδα.
Ενσωμάτωση εξαρτήσεων (Dependency Injection) — είναι ένα πρότυπο στο οποίο ένα αντικείμενο λαμβάνει τις εξαρτήσεις του από έξω, αντί να τις δημιουργεί μόνο του. Το DI υλοποιεί την αρχή της αντιστροφής ελέγχου (IoC): η κλάση δεν είναι υπεύθυνη για τη δημιουργία των δικών της εξαρτήσεων, αλλά τις δηλώνει μέσω κατασκευαστή, μεθόδου ή πεδίου. Η ενσωμάτωση μέσω κατασκευαστή (constructor injection) θεωρείται η πλέον προτιμώμενη, καθώς εγγυάται ότι το αντικείμενο δημιουργείται σε έγκυρη κατάσταση.
| Τύπος ενσωμάτωσης | Σύνταξη Dagger | Πότε χρησιμοποιείται |
|---|---|---|
| Constructor injection | @Inject constructor | Κύριος τρόπος — για όλες τις δικές σας κλάσεις |
| Field injection | @Inject lateinit var | Μόνο για Android components (Activity, Fragment) |
| Method injection | @Inject fun bind() | Για post-construct αρχικοποίηση |
Τα κύρια πλεονεκτήματα του DI περιλαμβάνουν τη δυνατότητα δοκιμής (οι εξαρτήσεις μπορούν να αντικατασταθούν με mock αντικείμενα), χαλαρή σύζευξη (οι κλάσεις εξαρτώνται από interfaces, όχι από υλοποιήσεις) και ρητή διαχείριση του κύκλου ζωής αντικειμένων μέσω scopes. Dagger εγγυάται αυτόματα ότι ένα αντικείμενο δημιουργείται μία φορά εντός του scope του και καταστρέφεται κατά την έξοδο από το scope.
Component — το κεντρικό στοιχείο του γράφου εξαρτήσεων Dagger. Είναι ένα interface με annotation @Component που περιγράφει τη γέφυρα μεταξύ Module και στόχων ενσωμάτωσης. Το Dagger παράγει την υλοποίηση του Component (π.χ. DaggerAppComponent) στο στάδιο μεταγλώττισης. Το Component καθορίζει ποιοι τύποι είναι διαθέσιμοι για ενσωμάτωση, μέσω abstract μεθόδων που επιστρέφουν τους απαιτούμενους τύπους ή μέσω inject μεθόδων που δέχονται ένα αντικείμενο για field injection.
// Module: παρέχει εξαρτήσεις που το Dagger δεν μπορεί να δημιουργήσει μόνο του
@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: συνδέει το Module και τους στόχους ενσωμάτωσης
@Component(modules = [NetworkModule::class])
interface AppComponent {
fun inject(activity: MainActivity)
fun getApiService(): ApiService
}
@Module — είναι μια κλάση που περιέχει μεθόδους με @Provides που επιστρέφουν στιγμιότυπα εξαρτήσεων. Το Module χρησιμοποιείται για τύπους που το Dagger δεν μπορεί να δημιουργήσει αυτόματα: βιβλιοθήκες τρίτων (OkHttp, Retrofit), αντικείμενα με παραμέτρους κατασκευαστή, interfaces με επιλογή υλοποίησης. @Binds — εναλλακτική λύση για το @Provides σε περιπτώσεις όπου η μέθοδος επιστρέφει ένα interface και δέχεται μία μόνο υλοποίηση: το Dagger παράγει άμεση μετατροπή (cast) χωρίς να καλεί τη μέθοδο.
@Scope καθορίζει τη διάρκεια ζωής του αντικειμένου στον γράφο εξαρτήσεων. @Singleton — το αντικείμενο δημιουργείται μία φορά για ολόκληρη την εφαρμογή. @ActivityScoped — το αντικείμενο ζει όσο ζει το Activity. @FragmentScoped — όσο ζει το Fragment. Χωρίς scope, το Dagger δημιουργεί νέο στιγμιότυπο σε κάθε ενσωμάτωση. @Reusable — scope για αντικείμενα που δεν χρειάζεται να είναι singleton αλλά η δημιουργία τους είναι δαπανηρή — το Dagger μπορεί να αποθηκεύσει το στιγμιότυπο σε cache, αλλά δεν το εγγυάται.
Hilt απλοποιεί τη ρύθμιση του Dagger για Android χάρη σε προκαθορισμένα components και αυτόματη παραγωγή του βασικού γράφου. Το annotation @HiltAndroidApp στην κλάση Application ενεργοποιεί την παραγωγή του Hilt component. Χωρίς αυτό το annotation το Hilt δεν λειτουργεί — είναι υποχρεωτικό για κάθε Android εφαρμογή που χρησιμοποιεί Hilt. Το @HiltAndroidApp δημιουργεί το γονικό component SingletonComponent, από το οποίο κληρονομούν όλα τα άλλα components της εφαρμογής.
@HiltAndroidApp
class MyApplication : Application()
@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
@Inject lateinit var apiService: ApiService
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// το apiService έχει ήδη ενσωματωθεί πριν από την κλήση 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 — annotation για Activity, Fragment, Service, BroadcastReceiver και View. Παράγει ένα Hilt component για κάθε τύπο: το @AndroidEntryPoint στο Activity δημιουργεί το ActivityComponent που κληρονομεί από το SingletonComponent. Το θυγατρικό component λαμβάνει αυτόματα όλες τις εξαρτήσεις του γονέα. Το field injection με @Inject lateinit var είναι διαθέσιμο μόνο σε κλάσεις με annotation @AndroidEntryPoint — σε συνηθισμένες κλάσεις χρησιμοποιείται constructor injection.
@InstallIn υποδεικνύει σε ποιο Hilt component εγκαθίσταται το module. Το NetworkModule με @InstallIn(SingletonComponent::class) είναι διαθέσιμο σε ολόκληρη την εφαρμογή. Το Module με @InstallIn(ActivityComponent::class) — μόνο στο Activity. Αυτό απομονώνει τους γράφους εξαρτήσεων: τα modules ειδικά για Activity δεν είναι ορατά στο Fragment και ViewModel, αποτρέποντας την τυχαία χρήση μη επιτρεπόμενων εξαρτήσεων. @ApplicationContext — το ενσωματωμένο χαρακτηριστικό του Hilt για λήψη του περιβάλλοντος της εφαρμογής.
Όταν απαιτείται η ενσωμάτωση δύο διαφορετικών υλοποιήσεων του ίδιου interface, χρησιμοποιούνται χαρακτηριστικά (qualifiers). Το Hilt υποστηρίζει @Named για αναγνωριστικά κειμένου και προσαρμοσμένα annotations με @Qualifier. Για παράδειγμα, @Named("baseUrl") και @Named("imageBaseUrl") για διαφορετικές διαμορφώσεις κειμένου. Τα προσαρμοσμένα χαρακτηριστικά προτιμώνται έναντι του @Named λόγω ελέγχου κατά τη μεταγλώττιση — ένα λανθασμένο όνομα κειμένου δεν θα ανιχνευθεί μέχρι το runtime.
@HiltViewModel — annotation που αντικαθιστά το χειροκίνητο εργοστάσιο ViewModelProvider.Factory. Μια κλάση με annotation @HiltViewModel με @Inject constructor λαμβάνει αυτόματα όλες τις εξαρτήσεις μέσω Dagger. Το Hilt παράγει ViewModelFactory που χρησιμοποιείται από το Jetpack ViewModelProvider. Χωρίς Hilt, ο προγραμματιστής θα έπρεπε να γράψει το εργοστάσιο χειροκίνητα, μεταβιβάζοντας κάθε παράμετρο από το Activity ή το 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()
}
}
}
// Στο Activity — το Hilt δημιουργεί αυτόματα ViewModel
@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
private val viewModel: MainViewModel by viewModels()
}
ViewModelScoped — scope του Hilt για εξαρτήσεις που ζουν όσο ζει το ViewModel. Αν δύο ViewModel του ίδιου τύπου ενσωματώνουν την ίδια εξάρτηση @ViewModelScoped, κάθε ένα λαμβάνει το δικό του στιγμιότυπο. Αυτό διαφοροποιεί το @ViewModelScoped από το @ActivityScoped, όπου ένα Activity λαμβάνει ένα στιγμιότυπο για όλα τα fragments. Για εξαρτήσεις ειδικές για ViewModel (π.χ. SavedStateHandle) χρησιμοποιήστε @HiltViewModel με @Inject constructor(savedStateHandle: SavedStateHandle).
Το Hilt υποστηρίζει assisted injection μέσω της βιβλιοθήκης Hilt Extensions. Το assisted injection επιτρέπει τη μεταβίβαση παραμέτρων στον κατασκευαστή κατά την ενσωμάτωση, όταν μέρος των εξαρτήσεων είναι γνωστό μόνο κατά το runtime (π.χ. ID χρήστη από intent). Για assisted injection χρησιμοποιείται το @AssistedInject σε συνδυασμό με παραμέτρους @Assisted. Το Hilt παράγει AssistedFactory, η οποία μπορεί να ενσωματωθεί με τυπικό τρόπο.
Καθαρό Dagger απαιτεί χειροκίνητη δημιουργία Component, καθορισμό scope και ρύθμιση ενσωμάτωσης σε κάθε Android component. Ο προγραμματιστής δημιουργεί AppComponent, ActivityComponent, FragmentComponent και διαχειρίζεται τις σχέσεις τους μέσω @Subcomponent. Αυτή η προσέγγιση παρέχει μέγιστο έλεγχο αλλά απαιτεί σημαντική ποσότητα boilerplate κώδικα. Το Dagger χρησιμοποιείται σε μεγάλα projects όπου απαιτείται προσαρμοσμένη αρχιτεκτονική DI ή σε μη Android Java/Kotlin projects.
Hilt αυτοματοποιεί το boilerplate: ένα @HiltAndroidApp, ένα @AndroidEntryPoint για κάθε component, προκαθορισμένα scopes. Το Hilt συνιστάται από την Google για όλα τα νέα Android projects. Η μετεγκατάσταση από Dagger σε Hilt περιλαμβάνει αντικατάσταση του Component με @InstallIn, αντικατάσταση του @Subcomponent με προκαθορισμένα Hilt components και αντικατάσταση του χειροκίνητου ViewModelProvider.Factory με @HiltViewModel. Οι περισσότερες κλάσεις @Module μεταφέρονται με προσθήκη @InstallIn χωρίς τροποποίηση των μεθόδων @Provides.
| Χαρακτηριστικό | Dagger | Hilt |
|---|---|---|
| Ρύθμιση | Χειροκίνητη: Component, Subcomponent, Builder | Αυτόματη: @HiltAndroidApp, @AndroidEntryPoint |
| Android components | Κανένα προκαθορισμένο | 12+ ενσωματωμένα components |
| ViewModel | Χειροκίνητο εργοστάσιο | @HiltViewModel + @Inject constructor |
| Πολλαπλά modules | Μέσω @Component(dependencies) | Μέσω @InstallIn + συγκέντρωσης |
| Πολυπλοκότητα | Υψηλή — απαιτεί εμπειρία | Χαμηλή — διαισθητική |
| Ευελιξία | Μέγιστη | Τυπική (καλύπτει 95% σεναρίων) |
Περιορισμοί Hilt: η βιβλιοθήκη υποστηρίζει μόνο Android (δεν είναι κατάλληλη για καθαρά server Java projects), επιβάλλει συγκεκριμένη δομή components (δύσκολο να παρακαμφθεί) και προσθέτει εξάρτηση από το android.hilt:hilt-navigation-compose για Jetpack Compose projects. Για εφαρμογές Compose, το Hilt παρέχει @HiltViewModel διαθέσιμο στο Composable μέσω hiltViewModel() — χωρίς χειροκίνητη παροχή ViewModel από το Activity.
Συχνές ερωτήσεις
Dagger — βασικό compile-time DI framework με χειροκίνητη ρύθμιση Component και Module. Hilt — επίπεδο Android που αυτοματοποιεί τη δημιουργία components και την ενσωμάτωση με τον κύκλο ζωής των Activity, Fragment, ViewModel, Service και BroadcastReceiver.
@HiltAndroidApp ενεργοποιεί την παραγωγή του Hilt component για το Application. Χωρίς αυτό το annotation, το Hilt δεν μπορεί να δημιουργήσει το βασικό component SingletonComponent, από το οποίο κληρονομούν όλα τα ActivityComponent, FragmentComponent και ViewModelComponent. Το annotation είναι υποχρεωτικό για κάθε Hilt project.
Hilt Navigation παρέχει @HiltViewModel για το ViewModel στο NavBackStackEntry και hiltNavGraphViewModels() για το scoping του ViewModel εντός του γράφου πλοήγησης. Η βιβλιοθήκη android.hilt:hilt-navigation-fragment δημιουργεί αυτόματα ViewModel για κάθε NavBackStackEntry.
Χρησιμοποιήστε @ApplicationContext για το περιβάλλον της εφαρμογής ή @ActivityContext για το περιβάλλον του Activity. Το Hilt παρέχει αυτά τα χαρακτηριστικά ενσωματωμένα στη βιβλιοθήκη android.hilt:hilt-android. Το @ActivityContext είναι διαθέσιμο μόνο σε modules εγκατεστημένα στο ActivityComponent.
@Binds — μια αποδοτική εναλλακτική για το @Provides, όταν η μέθοδος δέχεται ακριβώς μία παράμετρο και επιστρέφει τον τύπο της ως interface. Το @Binds παράγει άμεση μετατροπή (cast) χωρίς να καλεί τη μέθοδο, μειώνοντας την ποσότητα παραγόμενου κώδικα και αυξάνοντας την απόδοση κατά την ενσωμάτωση.
Περίληψη
Θα αναπτύξουμε μια εφαρμογή για κινητά έτοιμη για χρήση
Η IT Sectr δημιουργεί εφαρμογές iOS και Android για νεοφυείς επιχειρήσεις και επιχειρήσεις από το 2017. Θα σας συμβουλεύσουμε και θα προτείνουμε την καλύτερη λύση.
Διαβάστε επίσης