100% Free Forever
AI-Powered Learning
Industry Expert Content
Certificates & Badges
Learn At Your Own Pace
Kotlin

Dependency Injection with Hilt

Hilt is Android's recommended dependency injection library built on Dagger, providing standard components and annotations that wire dependencies into Activities, Fragments, ViewModels, and Compose.

Architecture & ViewModelIntermediate10 min readJul 8, 2026
Analogies

Dependency Injection with Hilt

Dependency injection (DI) is a design pattern where an object receives its dependencies from an external source rather than constructing them itself. Instead of a ViewModel calling Retrofit.Builder().build() internally, it declares a constructor parameter of type ApiService and lets something else supply it. This decouples classes from the concrete details of how their dependencies are created, which makes code easier to test (you can inject a fake) and easier to reconfigure (swap an implementation without touching every call site). Hilt is Google's opinionated DI library for Android, built on top of Dagger, that removes most of the Dagger boilerplate by generating standard components tied to Android's lifecycle owners.

🏏

Cricket analogy: A franchise doesn't make its overseas star build his own bat; the team's kit sponsor supplies it, just as a ViewModel declares an ApiService parameter instead of constructing Retrofit.Builder().build() itself, decoupling it from concrete setup.

Without a DI framework, a large app ends up hand-writing factories or service locators to pass dependencies down through constructors, which becomes unwieldy as the object graph grows. Hilt solves this by using annotation processing at compile time to generate the wiring code: you annotate classes with @Inject or @Module/@Provides, mark entry points like Application and Activity with @HiltAndroidApp/@AndroidEntryPoint, and Hilt generates a dependency graph that is validated at compile time — missing or ambiguous bindings are caught before the app ever runs, unlike a runtime service locator.

🏏

Cricket analogy: A team without a proper support staff ends up with players individually arranging their own physios, which collapses at scale; Hilt is the professional support staff that Dagger-generates automatically, validated before the season (compile time) rather than mid-match.

Core Annotations

@HiltAndroidApp goes on the Application class and bootstraps Hilt's code generation for the whole app. @AndroidEntryPoint goes on Activities, Fragments, Services, and BroadcastReceivers that need injected members. @Inject on a constructor tells Hilt how to build that class automatically; @Module combined with @InstallIn defines a class containing @Provides or @Binds methods for types Hilt cannot construct directly (e.g. interfaces, or third-party classes like Retrofit or Room's Database). @HiltViewModel on a ViewModel class, paired with @Inject constructor(...), lets you retrieve a fully wired ViewModel via the standard viewModel() or hiltViewModel() composable function with zero manual factory code.

🏏

Cricket analogy: @HiltAndroidApp is like appointing a chief selector for the whole squad, @AndroidEntryPoint is like naming which match-day XI members need kit issued, and @HiltViewModel with @Inject is like a ready-made playing eleven delivered with zero manual selection meetings.

kotlin
@Module
@InstallIn(SingletonComponent::class)
object NetworkModule {
    @Provides
    @Singleton
    fun provideRetrofit(): Retrofit =
        Retrofit.Builder()
            .baseUrl("https://api.example.com/")
            .addConverterFactory(MoshiConverterFactory.create())
            .build()

    @Provides
    @Singleton
    fun provideApiService(retrofit: Retrofit): ApiService =
        retrofit.create(ApiService::class.java)
}

@Module
@InstallIn(SingletonComponent::class)
abstract class RepositoryModule {
    @Binds
    abstract fun bindTodoRepository(impl: TodoRepositoryImpl): TodoRepository
}

@HiltViewModel
class TodoViewModel @Inject constructor(
    private val repository: TodoRepository
) : ViewModel() { /* ... */ }

@Composable
fun TodoRoute(viewModel: TodoViewModel = hiltViewModel()) {
    // fully wired ViewModel, no manual factory needed
}

Components and Scopes

Hilt predefines a hierarchy of components mirroring Android's lifecycle: SingletonComponent (Application-scoped, lives as long as the process), ActivityRetainedComponent (survives configuration changes, ideal for ViewModel-scoped dependencies), ViewModelComponent, ActivityComponent, FragmentComponent, and ViewComponent. Installing a binding with @InstallIn(SingletonComponent::class) and annotating it @Singleton means Hilt creates exactly one instance for the whole app; installing into ActivityComponent with @ActivityScoped ties the instance's lifetime to a single Activity. Choosing the right scope avoids both wasteful re-creation (e.g. rebuilding an OkHttpClient per screen) and lifecycle leaks (e.g. an Activity-scoped object being retained by a Singleton-scoped dependency).

🏏

Cricket analogy: SingletonComponent is like the national board that persists across every series, while ActivityComponent is a single Test match's ground staff; scoping an OkHttpClient at Singleton avoids hiring new groundstaff for every over, unlike a wastefully re-created ActivityScoped one.

Think of Hilt components as nested Russian dolls that mirror Android's own lifecycle: SingletonComponent (app) contains ActivityRetainedComponent (survives rotation) which contains ActivityComponent (one Activity instance) which contains ViewComponent (a single Composable/View). A dependency scoped to an outer doll is visible to all inner dolls, but never the reverse.

A frequent mistake is injecting an Activity-scoped or Context-holding dependency into a Singleton-scoped class. Because the Singleton outlives the Activity, it ends up holding a reference to a destroyed Activity, causing a memory leak. Always inject @ApplicationContext (not @ActivityContext) into Singleton-scoped classes, and let Hilt's compile-time checks catch scope mismatches early.

  • Hilt is Google's recommended DI library for Android, built on Dagger, with less boilerplate.
  • @HiltAndroidApp bootstraps the app; @AndroidEntryPoint enables injection in Activities/Fragments.
  • @Inject constructors let Hilt build classes automatically; @Module/@Provides or @Binds handle types Hilt can't construct directly.
  • @HiltViewModel + hiltViewModel() gives you a fully wired ViewModel in Compose with no manual factory.
  • Hilt components (SingletonComponent, ActivityRetainedComponent, etc.) map to Android lifecycle scopes.
  • Mismatched scopes — e.g. injecting an Activity Context into a Singleton — are a common source of memory leaks.

Practice what you learned

Was this page helpful?

Topics covered

#Kotlin#AndroidWithJetpackComposeStudyNotes#MobileDevelopment#DependencyInjectionWithHilt#Dependency#Injection#Hilt#Core#StudyNotes#SkillVeris