Ми стикаємося з проектами, де 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-коду.







