Dagger — это фреймворк внедрения зависимостей для Java и Kotlin, генерирующий DI-код на этапе компиляции через обработку аннотаций. Hilt — надстройка над Dagger для Android, упрощающая настройку компонентов и жизненного цикла. По данным Google, 2025, Hilt используется более чем в 70% Android-приложений из Google Play Top-100, поддерживая Activity, Fragment, ViewModel и Service через предопределённые компоненты. Оба фреймворка обеспечивают compile-time проверку графа зависимостей, исключая runtime-ошибки внедрения.
Главное
Dagger — это фреймворк внедрения зависимостей с генерацией кода на этапе компиляции (compile-time DI). Разработанный в Square и позже переданный Google, Dagger использует процессор аннотаций Java APT для анализа графа зависимостей и генерации фабричных классов. В отличие от runtime-DI (Guice, Koin), Dagger не использует рефлексию — весь код создаётся при компиляции, что обеспечивает максимальную производительность в рантайме и выявление ошибок на этапе сборки.
Hilt — это библиотека от Google, построенная поверх Dagger и оптимизированная для Android. Hilt предоставляет предопределённые компоненты, соответствующие жизненному циклу Android-компонентов: @SingletonComponent для Application, @ActivityComponent для Activity, @FragmentComponent для Fragment, @ViewModelComponent для ViewModel. Это устраняет рутинную настройку Component и Module, которая требуется в чистом Dagger. Hilt также генерирует граф зависимостей для каждого Android-компонента автоматически через @AndroidEntryPoint.
По данным Google I/O 2024, Hilt является рекомендованным решением для DI в Android-приложениях, написанных на Kotlin. Jetpack-библиотеки (Navigation, Room, WorkManager) имеют встроенную интеграцию с Hilt через @HiltViewModel и @HiltWorker. В проектах, не использующих Android (чистая Java/Kotlin библиотеки, серверные приложения), применяется чистый Dagger без Hilt-надстройки.
Без DI-фреймворка разработчик создаёт объекты вручную через конструкторы или фабрики, передавая зависимости по цепочке. Каждое новое требование — изменение сигнатуры всех конструкторов в цепочке. Dagger автоматизирует этот процесс: достаточно объявить, какой тип требуется (@Inject constructor), и Dagger создаст граф зависимостей, разрешив все вложенные типы. При изменении зависимостей Dagger обновляет сгенерированный код автоматически — ошибиться в цепочке невозможно.
Внедрение зависимостей (Dependency Injection) — это паттерн, при котором объект получает свои зависимости извне, а не создаёт их самостоятельно. DI реализует принцип инверсии управления (IoC): класс не отвечает за создание своих зависимостей, а декларирует их через конструктор, метод или поле. Внедрение через конструктор (constructor injection) считается наиболее предпочтительным, так как оно гарантирует, что объект создаётся в валидном состоянии.
| Тип внедрения | Синтаксис Dagger | Когда использовать |
|---|---|---|
| Constructor injection | @Inject constructor | Основной способ — для всех собственных классов |
| Field injection | @Inject lateinit var | Только для Android-компонентов (Activity, Fragment) |
| Method injection | @Inject fun bind() | Для post-construct инициализации |
Основные преимущества DI включают тестируемость (зависимости подменяются через mock-объекты), слабую связанность (классы зависят от интерфейсов, а не реализаций), и явное управление жизненным циклом объектов через scopes. Dagger автоматически гарантирует, что объект создаётся единожды в рамках своего скоупа и уничтожается при выходе из скоупа.
Component — центральный элемент графа зависимостей Dagger. Это интерфейс, аннотированный @Component, который описывает мост между Module и целями внедрения. Dagger генерирует реализацию Component (например, DaggerAppComponent) на этапе компиляции. Component определяет, какие типы доступны для внедрения, через абстрактные методы, возвращающие нужные типы, или через методы 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 и Injection-цели
@Component(modules = [NetworkModule::class])
interface AppComponent {
fun inject(activity: MainActivity)
fun getApiService(): ApiService
}
@Module — это класс, содержащий методы с @Provides, которые возвращают экземпляры зависимостей. Module используется для типов, которые Dagger не может создать автоматически: сторонние библиотеки (OkHttp, Retrofit), объекты с параметрами конструктора, интерфейсы с выбором реализации. @Binds — альтернатива @Provides для случаев, когда метод возвращает интерфейс и принимает единственную реализацию: Dagger генерирует прямой cast без вызова метода.
@Scope определяет время жизни объекта в графе зависимостей. @Singleton — объект создаётся один раз на всё приложение. @ActivityScoped — объект живёт, пока жива Activity. @FragmentScoped — пока жив Fragment. Без скоупа Dagger создаёт новый экземпляр при каждом внедрении. @Reusable — скоуп для объектов, которые не обязаны быть синглтонами, но их создание дорого — Dagger может кэшировать экземпляр, но не гарантирует этого.
Hilt упрощает настройку Dagger для Android за счёт предопределённых компонентов и автоматической генерации базового графа. Аннотация @HiltAndroidApp на Application-классе запускает генерацию Hilt-компонента. Без этой аннотации Hilt не работает — она обязательна для любого Android-приложения, использующего Hilt. @HiltAndroidApp создаёт родительский компонент SingletonComponent, от которого наследуются все остальные компоненты приложения.
@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 — аннотация для Activity, Fragment, Service, BroadcastReceiver и View. Она генерирует Hilt-компонент для каждого типа: @AndroidEntryPoint на Activity создаёт ActivityComponent, который наследуется от SingletonComponent. Дочерний компонент автоматически получает все зависимости родителя. Field injection с @Inject lateinit var доступен только в классах, аннотированных @AndroidEntryPoint — в обычных классах используется constructor injection.
@InstallIn указывает, в какой Hilt-компонент устанавливается модуль. NetworkModule с @InstallIn(SingletonComponent::class) доступен во всём приложении. Module с @InstallIn(ActivityComponent::class) — только в Activity. Это изолирует графы зависимостей: Activity-специфичные модули не видны в Fragment и ViewModel, предотвращая случайное использование недопустимых зависимостей. @ApplicationContext — встроенный квалификатор Hilt для получения Context приложения.
Когда требуется внедрить две разные реализации одного интерфейса, используются квалификаторы. Hilt поддерживает @Named для строковых идентификаторов и кастомные аннотации с @Qualifier. Например, @Named("baseUrl") и @Named("imageBaseUrl") для разных строковых конфигураций. Кастомные квалификаторы предпочтительнее @Named из-за проверки на этапе компиляции — неправильное имя строки не будет обнаружено до рантайма.
@HiltViewModel — аннотация, заменяющая ручную фабрику ViewModelProvider.Factory. Класс, аннотированный @HiltViewModel с @Inject constructor, автоматически получает все зависимости через Dagger. Hilt генерирует ViewModelFactory, используемую Jetpack ViewModelProvider. Без Hilt разработчику приходится писать фабрику вручную, передавая каждый параметр из Activity или фрагмента.
@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 — скоуп Hilt для зависимостей, которые живут, пока жив ViewModel. Если два ViewModel одного типа внедряют одну @ViewModelScoped-зависимость, каждая получает свой экземпляр. Это отличает @ViewModelScoped от @ActivityScoped, где одна Activity получает один экземпляр для всех фрагментов. Для ViewModel-специфичных зависимостей (например, SavedStateHandle) используется @HiltViewModel с @Inject constructor(savedStateHandle: SavedStateHandle).
Hilt поддерживает assisted injection через библиотеку Hilt Extensions. Assisted injection позволяет передавать параметры в конструктор во время внедрения, когда часть зависимостей известна только в рантайме (например, ID пользователя из интента). Для assisted injection используется @AssistedInject в комбинации с @Assisted-параметрами. Hilt генерирует AssistedFactory, которую можно внедрить стандартным способом.
Чистый Dagger требует ручного создания Component, определения скоупов и настройки инъекции в каждый Android-компонент. Разработчик создаёт AppComponent, ActivityComponent, FragmentComponent, управляет их связями через @Subcomponent. Этот подход даёт максимальный контроль, но требует значительного boilerplate-кода. Dagger используется в крупных проектах, где требуется нестандартная архитектура DI, или в не-Android Java/Kotlin-проектах.
Hilt автоматизирует boilerplate: один @HiltAndroidApp, один @AndroidEntryPoint для каждого компонента, предопределённые скоупы. Hilt рекомендуется Google для всех новых Android-проектов. Миграция с Dagger на Hilt включает замену Component на @InstallIn, замену @Subcomponent на предопределённые Hilt-компоненты и замену ручного ViewModelProvider.Factory на @HiltViewModel. Большинство @Module-классов переносятся с добавлением @InstallIn без изменения @Provides методов.
| Характеристика | Dagger | Hilt |
|---|---|---|
| Настройка | Ручная: Component, Subcomponent, Builder | Авто: @HiltAndroidApp, @AndroidEntryPoint |
| Android-компоненты | Нет предопределённых | 12+ встроенных компонентов |
| ViewModel | Ручная фабрика | @HiltViewModel + @Inject constructor |
| Multimodule | Через @Component(dependencies) | Через @InstallIn + агрегацию |
| Сложность | Высокая — нужен опыт | Низкая — интуитивно понятный |
| Гибкость | Максимальная | Стандартная (покрывает 95% сценариев) |
Ограничения Hilt: библиотека поддерживает только Android (не подходит для чисто серверных Java-проектов), накладывает определённую структуру компонентов (сложно переопределить), и добавляет зависимость от android.hilt:hilt-navigation-compose для Jetpack Compose-проектов. Для Compose-приложений Hilt предоставляет @HiltViewModel, доступный в Composable через hiltViewModel() — без ручного предоставления ViewModel из Activity.
Часто задаваемые вопросы
Dagger — базовый compile-time DI фреймворк с ручной настройкой Component и Module. Hilt — надстройка для Android, автоматизирующая создание компонентов и интеграцию с жизненным циклом Activity, Fragment, ViewModel, Service и BroadcastReceiver.
@HiltAndroidApp включает генерацию Hilt-компонента для Application. Без этой аннотации Hilt не может создать базовый компонент SingletonComponent, от которого наследуются все ActivityComponent, FragmentComponent и ViewModelComponent. Аннотация обязательна для любого Hilt-проекта.
Hilt Navigation предоставляет @HiltViewModel для ViewModel в NavBackStackEntry и hiltNavGraphViewModels() для скоупинга ViewModel в рамках навигационного графа. Библиотека android.hilt:hilt-navigation-fragment автоматически создаёт ViewModel для каждого NavBackStackEntry.
Используйте @ApplicationContext для контекста приложения или @ActivityContext для контекста Activity. Hilt предоставляет эти квалификаторы встроенными в библиотеку android.hilt:hilt-android. @ActivityContext доступен только в модулях, установленных в ActivityComponent.
@Binds — эффективная альтернатива @Provides, когда метод принимает ровно один параметр и возвращает его тип как интерфейс. @Binds генерирует прямой cast без вызова метода, что уменьшает количество сгенерированного кода и повышает производительность при внедрении.
Итоги
Мы разработаем мобильное приложение под ключ
IT Sectr создаёт приложения для iOS и Android для стартапов и бизнеса с 2017 года. Мы проконсультируем вас и предложим наилучшее решение.
Читайте также