# How Dependency Injection Is Configured with Hilt Across the GSYGitHubApp Compose Modules

> Discover how Hilt configures dependency injection for GSYGitHubApp Compose modules. Learn about @HiltAndroidApp, SingletonComponent, and @HiltViewModel for efficient dependency management.

- Repository: [Shuyu Guo/gsygithubappcompose](https://github.com/carguo/gsygithubappcompose)
- Tags: internals
- Published: 2026-02-26

---

**The GSYGitHubApp uses `@HiltAndroidApp` in the Application class to bootstrap a compile-time validated dependency graph, with core singletons provided via `SingletonComponent`-scoped modules and feature ViewModels receiving dependencies through constructor injection annotated with `@HiltViewModel`.**

The **gsygithubappcompose** repository demonstrates a production-grade Android architecture where **dependency injection configured with Hilt** unifies the multi-module codebase. By leveraging Dagger’s compile-time code generation, the app ensures thread-safe singletons for networking and persistence while allowing feature modules to consume dependencies without manual factory boilerplate.

## Application-Level Bootstrapping with @HiltAndroidApp

Hilt requires a single annotated Application class to trigger its code generation across all modules. In [`app/src/main/java/com/shuyu/gsygithubappcompose/GSYApplication.kt`](https://github.com/carguo/gsygithubappcompose/blob/main/app/src/main/java/com/shuyu/gsygithubappcompose/GSYApplication.kt), the `@HiltAndroidApp` annotation generates the base component that wires together all modules and makes the **Application** context injectable:

```kotlin
package com.shuyu.gsygithubappcompose

import android.app.Application
import dagger.hilt.android.HiltAndroidApp

@HiltAndroidApp
class GSYApplication : Application()

```

This annotation creates the `SingletonComponent` that lives for the duration of the application process and serves as the root for all dependency provision.

## Core Singleton Modules in SingletonComponent

All foundational dependencies are installed in `SingletonComponent`, guaranteeing a **single instance** for the entire process regardless of which module requests them.

### NetworkModule – Providing Retrofit and OkHttp

Located at [`core/network/src/main/java/com/shuyu/gsygithubappcompose/core/network/di/NetworkModule.kt`](https://github.com/carguo/gsygithubappcompose/blob/main/core/network/src/main/java/com/shuyu/gsygithubappcompose/core/network/di/NetworkModule.kt), this module constructs the HTTP stack:

```kotlin
@Module
@InstallIn(SingletonComponent::class)
object NetworkModule {

    @Provides
    @Singleton
    fun provideOkHttpClient(tokenInterceptor: TokenInterceptor): OkHttpClient { 
        // Configuration logic
    }

    @Provides
    @Singleton
    fun provideRetrofit(okHttpClient: OkHttpClient): Retrofit { 
        // Builder configuration
    }

    @Provides
    @Singleton
    fun provideGitHubApiService(retrofit: Retrofit): GitHubApiService { 
        retrofit.create(GitHubApiService::class.java)
    }
}

```

The `TokenInterceptor` itself is injected into the provider method, demonstrating how modules can depend on other bindings within the same component.

### DatabaseModule – Room Database and DAOs

The [`core/database/src/main/java/com/shuyu/gsygithubappcompose/core/database/di/DatabaseModule.kt`](https://github.com/carguo/gsygithubappcompose/blob/main/core/database/src/main/java/com/shuyu/gsygithubappcompose/core/database/di/DatabaseModule.kt) file provides the Room database and all Data Access Objects as singletons:

```kotlin
@Module
@InstallIn(SingletonComponent::class)
object DatabaseModule {

    @Provides
    @Singleton
    fun provideAppDatabase(@ApplicationContext context: Context): AppDatabase {
        return Room.databaseBuilder(
            context, 
            AppDatabase::class.java, 
            "gsy_github_db"
        )
        .fallbackToDestructiveMigration(true)
        .build()
    }

    @Provides
    @Singleton
    fun provideUserDao(db: AppDatabase) = db.userDao()

    @Provides
    @Singleton
    fun provideRepositoryDao(db: AppDatabase) = db.repositoryDao()
}

```

All DAOs share the same `AppDatabase` instance, ensuring thread-safe database access throughout the app lifecycle.

### DataStoreModule – Binding Interfaces to Implementations

Located in [`core/common/src/main/java/com/shuyu/gsygithubappcompose/core/common/di/DataStoreModule.kt`](https://github.com/carguo/gsygithubappcompose/blob/main/core/common/src/main/java/com/shuyu/gsygithubappcompose/core/common/di/DataStoreModule.kt), this module demonstrates the mixed use of `@Binds` for interfaces and `@Provides` for concrete classes:

```kotlin
@Module
@InstallIn(SingletonComponent::class)
abstract class DataStoreModule {

    @Binds
    abstract fun bindUserPreferencesDataStore(
        impl: UserPreferencesDataStore
    ): IUserPreferencesDataStore

    companion object {
        @Provides
        @Singleton
        fun provideLanguageDataStore(
            @ApplicationContext ctx: Context
        ) = LanguageDataStore(ctx)
    }
}

```

The `@Binds` annotation efficiently links the interface to its implementation without boilerplate instantiation code, while the companion object handles concrete class provision.

### CommonModule – Utility Bindings

The [`core/common/src/main/java/com/shuyu/gsygithubappcompose/core/common/di/CommonModule.kt`](https://github.com/carguo/gsygithubappcompose/blob/main/core/common/src/main/java/com/shuyu/gsygithubappcompose/core/common/di/CommonModule.kt) binds utility interfaces like `StringResourceProvider`:

```kotlin
@Module
@InstallIn(SingletonComponent::class)
abstract class CommonModule {

    @Binds
    @Singleton
    abstract fun bindStringResourceProvider(
        impl: StringResourceProviderImpl
    ): StringResourceProvider
}

```

## Feature-Level Dependency Injection with @HiltViewModel

Every feature module leverages `@HiltViewModel` to receive repositories and services automatically. In [`feature/welcome/src/main/java/com/shuyu/gsygithubappcompose/feature/welcome/WelcomeViewModel.kt`](https://github.com/carguo/gsygithubappcompose/blob/main/feature/welcome/src/main/java/com/shuyu/gsygithubappcompose/feature/welcome/WelcomeViewModel.kt):

```kotlin
@HiltViewModel
class WelcomeViewModel @Inject constructor(
    private val userRepository: UserRepository
) : ViewModel() {
    // ViewModel logic utilizing injected repository
}

```

The `@Inject` annotation on the constructor instructs Hilt to resolve the `UserRepository` parameter from the graph. This pattern repeats across the codebase:

- **TrendingViewModel** – Injects `TrendingRepository`
- **LoginViewModel** – Injects `UserRepository` and `NotificationRepository`
- **RepoDetailInfoViewModel** – Injects `RepositoryRepository` and `EventRepository`

## Wiring Injection Points in the UI Layer

Activities and Fragments use `@AndroidEntryPoint` to enable injection. For example, fragments obtain ViewModels using the `by viewModels()` delegate:

```kotlin
@AndroidEntryPoint
class WelcomeFragment : Fragment(R.layout.fragment_welcome) {

    private val viewModel: WelcomeViewModel by viewModels()
}

```

For direct field injection in Activities (less common for ViewModels but supported):

```kotlin
@AndroidEntryPoint
class MainActivity : AppCompatActivity() {

    @Inject
    lateinit var userRepository: UserRepository
}

```

## Summary

- **Bootstrap**: `GSYApplication` annotated with `@HiltAndroidApp` initializes the global DI graph.
- **Core Modules**: `NetworkModule`, `DatabaseModule`, `DataStoreModule`, and `CommonModule` install singletons into `SingletonComponent` for application-scoped instances.
- **Pattern**: The project uses `@Binds` for interface implementations and `@Provides` for concrete class construction.
- **Feature Layer**: `@HiltViewModel` with constructor injection eliminates manual ViewModel factory creation.
- **Compile-Time Safety**: All dependencies are resolved at compile time, preventing runtime null pointer exceptions in the dependency graph.

## Frequently Asked Questions

### What is the entry point for Hilt in this Android app?

The entry point is the `GSYApplication` class in [`app/src/main/java/com/shuyu/gsygithubappcompose/GSYApplication.kt`](https://github.com/carguo/gsygithubappcompose/blob/main/app/src/main/java/com/shuyu/gsygithubappcompose/GSYApplication.kt), annotated with `@HiltAndroidApp`. This annotation triggers Hilt's code generation and creates the `SingletonComponent` that manages all application-scoped dependencies.

### How does Hilt provide singleton instances across different modules?

By annotating provider methods with `@Singleton` and installing modules in `SingletonComponent::class`, Hilt ensures only one instance exists per application lifecycle. For example, the `OkHttpClient`, `AppDatabase`, and various DAOs are all scoped to `SingletonComponent`, making them available as singletons throughout the multi-module architecture.

### Why does the project use both @Binds and @Provides in DataStoreModule?

`@Binds` is used for interface-to-implementation mapping (like `IUserPreferencesDataStore` to `UserPreferencesDataStore`) because it generates more efficient code and requires less boilerplate than `@Provides`. However, `@Provides` is necessary for concrete classes like `LanguageDataStore` that require constructor parameters or manual instantiation logic. The module uses a `companion object` to contain `@Provides` methods within an `abstract class` that contains `@Binds` methods.

### How do ViewModels obtain their dependencies in the feature modules?

Feature ViewModels use the `@HiltViewModel` annotation combined with constructor injection (`@Inject`). When a Fragment or Activity requests the ViewModel via `by viewModels()`, Hilt automatically supplies the required repository dependencies (such as `UserRepository` or `TrendingRepository`) that are defined in the core modules and scoped to `SingletonComponent`.