Skip to content

Android Hilt Module β€” Complete Guide

DodaTech Updated 2026-06-24 2 min read

In this tutorial, you'll learn about Android Hilt Module. We cover key concepts, practical examples, and best practices to help you understand and apply this topic effectively.

The Problem

Your Hilt module doesn't provide the expected dependency, has multiple conflicting bindings, or the wrong scope causes memory leaks.

Wrong Approach ❌

@Module
@InstallIn(SingletonComponent::class)
object BadModule {
    // Provide interface without @Binds β€” using @Provides unnecessarily
    @Provides
    fun provideRepository(api: ApiService): UserRepository {
        return UserRepositoryImpl(api) // Concrete type
    }
}
// Two bindings for the same type β€” ambiguous
@Provides fun provideA(): Logger = FileLogger()
@Provides fun provideB(): Logger = CloudLogger() // Ambiguous!

Output: DuplicateBinding error or unnecessary @Provides for simple bindings.

Right Approach βœ…

@Module
@InstallIn(SingletonComponent::class)
abstract class GoodModule {
    // @Binds for interface-to-implementation
    @Binds
    @Singleton
    abstract fun bindRepository(impl: UserRepositoryImpl): UserRepository

    @Binds
    abstract fun bindLogger(impl: FileLogger): Logger
}

@Module
@InstallIn(SingletonComponent::class)
object GoodProviderModule {
    // @Provides for third-party or complex creation
    @Provides
    @Singleton
    fun provideOkHttpClient(): OkHttpClient {
        return OkHttpClient.Builder()
            .connectTimeout(30, TimeUnit.SECONDS)
            .build()
    }

    @Provides
    @Singleton
    fun provideRetrofit(client: OkHttpClient): Retrofit {
        return Retrofit.Builder()
            .baseUrl("https://api.example.com")
            .client(client)
            .addConverterFactory(GsonConverterFactory.create())
            .build()
    }
}

// Qualifier for multiple same-type bindings
@Qualifier
annotation class CloudLogger

@Qualifier
annotation class FileLogger

@Provides @CloudLogger fun provideCloudLogger(): Logger = CloudLogger()
@Provides @FileLogger fun provideFileLogger(): Logger = FileLogger()

Output: Clean module structure with proper scoping and qualifiers.

Prevention

  • Use @Binds for interfaceβ†’impl, @Provides for complex creation.
  • Use @Qualifier annotations to disambiguate same-type bindings.
  • Scope singletons with @Singleton in SingletonComponent.
  • Keep modules in separate files by feature/concern.

Common Mistakes with hilt module

  1. Using foldl instead of foldl' causing stack overflow on large lists
  2. Forgetting deriving (Show, Eq) on custom data types needed for debugging
  3. Placing the wildcard pattern first in case expressions, making all subsequent patterns unreachable

These mistakes appear frequently in real-world Android code. DodaTech's contributors have identified these patterns through analysis of open-source projects and production systems.

Practice Exercise

Write a pure function that safely divides two integers using Maybe, then test it with edge cases like division by zero and negative numbers.

This exercise reinforces the concepts covered in this guide. Try implementing it before checking online solutions.

FAQ

### What is the difference between @Binds and @Provides?

@Binds is for interface bindings (abstract method, no body), more efficient. @Provides is for concrete creation (needs implementation). Both tell Hilt how to satisfy a dependency.

### How do I use @Qualifier?

Define an annotation with @Qualifier, then annotate both the provider and the injection site with the same qualifier. Hilt provides @Named("name") as a built-in qualifier.

### What is the correct scope for my module?

SingletonComponent for app-wide singletons. ViewModelComponent for ViewModel-scoped deps. ActivityComponent for Activity-scoped deps. FragmentComponent for Fragment-scoped deps.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro