Мы сталкиваемся с проектами, где Dagger 2 добавляют в середине разработки. Результат — запутанный граф, утечки памяти и баги, которые невозможно воспроизвести локально. Dagger генерирует код во время компиляции — никакой рефлексии, но за эту безопасность платим архитектурной дисциплиной. Наш опыт: настройка с нуля занимает 3–5 дней, а рефакторинг хаотичного внедрения — от недели. За более чем 5 лет работы мы настроили Dagger на 30+ проектах разного масштаба — от стартапов до enterprise-приложений. Гарантируем чистый DI-код без сюрпризов в рантайме. Получите консультацию по вашему проекту: закажите анализ текущей архитектуры.
Как правильно спроектировать граф компонентов?
Стандартная схема для большого приложения: AppComponent (Singleton) → ActivityComponent (PerActivity) → FragmentComponent (PerFragment). Каждый уровень — сабкомпонент или зависимый компонент. Ошибка архитектуры — помещать все зависимости в AppComponent, что увеличивает время сборки и усложняет тестирование.
@Singleton @Component(modules = [AppModule::class, NetworkModule::class, DatabaseModule::class]) interface AppComponent { fun inject(app: App) fun activityComponentBuilder(): ActivityComponent.Builder } @Module class NetworkModule { @Provides @Singleton fun provideOkHttpClient(): OkHttpClient { return OkHttpClient.Builder() .addInterceptor(AuthInterceptor()) .connectTimeout(30, TimeUnit.SECONDS) .build() } @Provides @Singleton fun provideRetrofit(client: OkHttpClient): Retrofit { return Retrofit.Builder() .baseUrl(BuildConfig.API_URL) .client(client) .addConverterFactory(GsonConverterFactory.create()) .build() } } Почему скоупы — частая причина трудноуловимых багов?
Самая частая ошибка — неправильные скоупы. Если UserRepository объявлен @Singleton, а AuthToken внутри него хранится в памяти, то после logout без пересоздания компонента старый токен остаётся в живом объекте. Это приводит к запросам с чужим токеном — продакшн-баг, который воспроизводится только при конкретном сценарии. Решение: @Singleton-компоненты не должны содержать изменяемое состояние, зависящее от сессии пользователя. Сессионные зависимости выносятся в @UserScope:
@Scope @Retention(AnnotationRetention.RUNTIME) annotation class UserScope @UserScope @Subcomponent(modules = [UserModule::class]) interface UserComponent { @Subcomponent.Factory interface Factory { fun create(@BindsInstance userId: String): UserComponent } fun inject(profileFragment: ProfileFragment) } UserComponent создаётся после логина, уничтожается после logout. Все зависимости, привязанные к пользователю, живут ровно столько, сколько нужно. Ниже таблица типичных ошибок:
| Типичная ошибка | Последствие | Решение |
|---|---|---|
| Изменяемое состояние в @Singleton | Утечка данных при logout | Вынос в @UserScope |
| @Singleton зависимость с медленной инициализацией | Замедление старта приложения | Использовать @Lazy или Provider |
| Отсутствие @BindsInstance | Необходимость ручного создания компонента | Добавлять @BindsInstance для динамических параметров |
Дополнительно стоит упомянуть, что неправильное использование скоупов может привести к утечкам памяти из-за случайного захвата Activity контекста в @Singleton. Мы всегда проверяем граф на такие сценарии.
Multibindings и плагинная архитектура
@IntoMap с @ViewModelKey — паттерн для инжекции ViewModels через ViewModelProvider.Factory. Dagger создаёт Map<Class<out ViewModel>, Provider<ViewModel>>, фабрика выбирает нужный класс. Без этого паттерна каждую ViewModel приходится отдельно объявлять в компоненте.
@Module abstract class ViewModelModule { @Binds @IntoMap @ViewModelKey(LoginViewModel::class) abstract fun bindLoginViewModel(vm: LoginViewModel): ViewModel @Binds @IntoMap @ViewModelKey(ProfileViewModel::class) abstract fun bindProfileViewModel(vm: ProfileViewModel): ViewModel } Этот же подход применим для плагинной архитектуры, где каждый модуль регистрирует свои зависимости через @IntoSet или @IntoMap. Мы использовали это в проекте с 10+ фича-модулями — Dagger автоматически собирает все предоставленные реализации.
Kapt и KSP: что выбрать для сборки?
Dagger 2 традиционно работает с kapt. Начиная с версии 2.50 доступна экспериментальная поддержка KSP, которая ускоряет инкрементальные сборки. На проекте с ~200 Dagger-аннотациями переход с kapt на KSP сократил время clean build с 4.5 до 2.8 минут — экономия времени разработки значительная.
// build.gradle.kts plugins { id("com.google.devtools.ksp") } dependencies { implementation("com.google.dagger:dagger:2.51") ksp("com.google.dagger:dagger-compiler:2.51") } | Параметр | Kapt | KSP (экспериментальный) |
|---|---|---|
| Время clean build (200 аннотаций) | 4.5 мин | 2.8 мин |
| Инкрементальная сборка | Обычная | Ускоренная |
| Поддержка Dagger | Полная | С 2.50, не все фичи |
Также стоит помнить про обфускацию: ProGuard/R8 могут удалить классы, созданные Dagger, если не добавить правила keep. Мы включаем это в конфигурацию.
Как тестировать приложение с Dagger?
Dagger и тесты — отдельная история. Стандартный подход: тестовые модули, которые заменяют продакшн-зависимости на фейки:
@Component(modules = [TestNetworkModule::class, DatabaseModule::class]) interface TestAppComponent : AppComponent @Module class TestNetworkModule { @Provides @Singleton fun provideApiService(): ApiService = FakeApiService() } В Espresso-тестах DaggerTestAppComponent подставляется вместо основного в App.appComponent до запуска теста. Без этой замены интеграционные тесты уходят на реальный сервер. Мы также используем TestCoroutineDispatcher для имитации задержек.
Когда выбирать Dagger 2, а не Hilt
Hilt — обёртка над Dagger с предзаданной структурой компонентов. Если нужны нестандартные скоупы, мультимодульный граф с независимыми компонентами или Dagger уже в проекте — Dagger 2 даёт полный контроль. Hilt быстрее стартует, но ограничивает в сложных архитектурах. В наших проектах мы часто комбинируем Dagger с Hilt в разных модулях.
Что входит в настройку Dagger 2 под ключ
- Анализ архитектуры и проектирование графа компонентов
- Создание модулей для сети, БД, shared preferences
- Настройка скоупов (Singleton, PerActivity, UserScope)
- Интеграция с ViewModel через мультибиндинг
- Конфигурация тестового компонента с фейками
- Переход с kapt на KSP при необходимости
- Документация по поддержке графа
Стоимость рассчитывается индивидуально. Настройка с нуля — 3–5 дней, рефакторинг — от недели. Получите консультацию: напишите нам, оценим ваш проект. Хотите внедрить Dagger 2 без головной боли? Свяжитесь с нами, мы проведём аудит вашего DI-кода.







