Setting Up Dependency Injection (Dagger 2) in Android App

We often encounter projects where Dagger 2 is added midway through development. The result is a tangled graph, memory leaks, and bugs that cannot be reproduced locally. Dagger generates code at compile time — no reflection, but this safety comes at the cost of architectural discipline. Our experienc

Development and support of all types of mobile applications:

Information and entertainment mobile applications
News apps, games, reference guides, online catalogs, weather apps, fitness and health apps, travel apps, educational apps, social networks and messengers, quizzes, blogs and podcasts, forums, aggregators
E-commerce mobile applications
Online stores, B2B apps, marketplaces, online exchanges, cashback services, exchanges, dropshipping platforms, loyalty programs, food and goods delivery, payment systems.
Business process management mobile applications
CRM systems, ERP systems, project management, sales team tools, financial management, production management, logistics and delivery management, HR management, data monitoring systems
Electronic services mobile applications
Classified ads platforms, online schools, online cinemas, electronic service platforms, cashback platforms, video hosting, thematic portals, online booking and scheduling platforms, online trading platforms

These are just some of the types of mobile applications we work with, and each of them may have its own specific features and functionality, tailored to the specific needs and goals of the client.

Showing 1 of 1All 1734 services
Setting Up Dependency Injection (Dagger 2) in Android App
Medium
~3-5 days

Our competencies:

Frequently Asked Questions

Latest works

  • image_mobile-applications_feedme_467_0.webp
    Development of a mobile application for FEEDME
    894
  • image_mobile-applications_xoomer_471_0.webp
    Development of a mobile application for XOOMER
    782
  • image_mobile-applications_rhl_428_0.webp
    Development of a mobile application for RHL
    1216
  • image_mobile-applications_zippy_411_0.webp
    Development of a mobile application for ZIPPY
    1079
  • image_mobile-applications_affhome_429_0.webp
    Development of a mobile application for Affhome
    1002
  • image_mobile-applications_flavors_409_0.webp
    Development of a mobile application for the FLAVORS company
    597

We often encounter projects where Dagger 2 is added midway through development. The result is a tangled graph, memory leaks, and bugs that cannot be reproduced locally. Dagger generates code at compile time — no reflection, but this safety comes at the cost of architectural discipline. Our experience: setup from scratch takes 3–5 days, and refactoring a chaotic implementation takes from a week. With over 5 years of work, we have set up Dagger on 30+ projects of various scales — from startups to enterprise applications. We guarantee clean DI code without runtime surprises. Get a consultation for your project: order an analysis of your current architecture.

How to properly design a component graph?

The typical schema for a large app: AppComponent (Singleton) → ActivityComponent (PerActivity) → FragmentComponent (PerFragment). Each level is a subcomponent or dependent component. The architectural mistake is placing all dependencies in AppComponent, which increases build times and complicates testing.

@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() } } 

Why are scopes a common cause of elusive bugs?

The most frequent mistake is incorrect scopes. If UserRepository is declared @Singleton and AuthToken inside it is stored in memory, then after logout, without recreating the component, the old token remains alive. This leads to requests with a stale token — a production bug that only reproduces under a specific scenario. Solution: @Singleton components should not contain mutable state that depends on the user session. Session-scoped dependencies should be moved to @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 is created after login and destroyed after logout. All dependencies bound to the user live exactly as long as needed. Below is a table of typical mistakes:

Typical Mistake Consequence Solution
Mutable state in @Singleton Data leak on logout Move to @UserScope
@Singleton dependency with slow initialization App startup delay Use @Lazy or Provider
Missing @BindsInstance Need to manually create component Add @BindsInstance for dynamic parameters

Additionally, improper use of scopes can cause memory leaks due to accidentally holding an Activity context in @Singleton. We always check the graph for such scenarios.

Multibindings and plugin architecture

@IntoMap with @ViewModelKey is a pattern for injecting ViewModels via ViewModelProvider.Factory. Dagger creates a Map<Class<out ViewModel>, Provider<ViewModel>>, and the factory selects the correct class. Without this pattern, each ViewModel must be declared separately in the component.

@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 } 

This same approach applies to plugin architecture, where each module registers its dependencies via @IntoSet or @IntoMap. We used this in a project with 10+ feature modules — Dagger automatically collects all provided implementations.

Kapt and KSP: which to choose for builds?

Dagger 2 traditionally works with kapt. Starting from version 2.50, experimental support for KSP is available, which speeds up incremental builds. On a project with ~200 Dagger annotations, switching from kapt to KSP reduced clean build time from 4.5 to 2.8 minutes — a significant development time saving.

// 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") } 
Parameter Kapt KSP (experimental)
Clean build time (200 annotations) 4.5 min 2.8 min
Incremental build Normal Accelerated
Dagger support Full Since 2.50, not all features

Also, don't forget about obfuscation: ProGuard/R8 can remove classes generated by Dagger if keep rules are not added. We include this in the configuration.

How to test an app with Dagger?

Dagger and tests are a separate story. The standard approach: test modules replace production dependencies with fakes:

@Component(modules = [TestNetworkModule::class, DatabaseModule::class]) interface TestAppComponent : AppComponent @Module class TestNetworkModule { @Provides @Singleton fun provideApiService(): ApiService = FakeApiService() } 

In Espresso tests, DaggerTestAppComponent is substituted for the main one in App.appComponent before the test runs. Without this replacement, integration tests hit the real server. We also use TestCoroutineDispatcher to simulate delays.

When to choose Dagger 2 over Hilt

Hilt is a wrapper over Dagger with a predefined component structure. If you need custom scopes, multi-module graphs with independent components, or Dagger is already in the project — Dagger 2 gives full control. Hilt gets you started faster, but limits complex architectures. In our projects, we often combine Dagger with Hilt in different modules.

What is included in a turnkey Dagger 2 setup

  • Analysis of architecture and component graph design
  • Creation of modules for network, database, shared preferences
  • Scope configuration (Singleton, PerActivity, UserScope)
  • ViewModel integration via multibinding
  • Test component setup with fakes
  • Migration from kapt to KSP if needed
  • Documentation for graph maintenance

The cost is calculated individually. Setup from scratch — 3–5 days, refactoring — from a week. Get a consultation: write to us, we'll assess your project. Want to implement Dagger 2 without headaches? Contact us, we'll audit your DI code.