agentsclimarketplace

Hilt di

Skill piyushverma0/android-agent-skills/skills/hilt-di

Hilt dependency injection for Android AI agents. Use this skill whenever setting up Hilt, writing @HiltViewModel, @AndroidEntryPoint, @HiltAndroidApp, @Module, @InstallIn, @Provides, @Binds, @Singleton, @ViewModelScoped, @ActivityScoped, @EntryPoint, @AssistedInject, @HiltWorker, @HiltAndroidTest, @BindValue, or debugging any Dagger/Hilt compile error. Also applies when choosing between @Provides vs @Binds, scoping dependencies, injecting into non-Hilt classes, or setting up Hilt for testing. Always use this skill before writing any @Module, @HiltViewModel, or @AndroidEntryPoint.From its SKILL.md

Install
npx -y skills add piyushverma0/android-agent-skills --skill hilt-di

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

One thing to look at

  • 14 stars14 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

SKILL.md

8.6 KB, ~1.8k tokens by cl100k_base, as published. Nobody here has run it

Hilt Dependency Injection

10 rules that fix what AI agents consistently get wrong with Hilt.

Setup — required before any injection works

// MyApplication.kt — REQUIRED, must be declared in AndroidManifest
@HiltAndroidApp
class MyApplication : Application()

// AndroidManifest.xml
<application android:name=".MyApplication" ...>
// build.gradle.kts — use KSP, never kapt
plugins {
    alias(libs.plugins.ksp)
    alias(libs.plugins.hilt)
}
dependencies {
    implementation(libs.hilt.android)
    ksp(libs.hilt.compiler)                          // ← ksp, not kapt
    implementation(libs.androidx.hilt.navigation.compose)
}

Rule 1: @Binds over @Provides for interface binding

// ✅ @Binds — zero runtime overhead, compiler-verified
@Module
@InstallIn(SingletonComponent::class)
abstract class RepositoryModule {
    @Binds
    @Singleton
    abstract fun bindItemRepository(impl: ItemRepositoryImpl): ItemRepository

    @Binds
    @Singleton
    abstract fun bindUserRepository(impl: UserRepositoryImpl): UserRepository
}

// ❌ @Provides for interface — works but is less efficient
@Module
@InstallIn(SingletonComponent::class)
object RepositoryModule {
    @Provides
    @Singleton
    fun provideItemRepository(impl: ItemRepositoryImpl): ItemRepository = impl
}

Rule 2: @Provides for third-party / constructed objects

// ✅ @Provides when you control construction
@Module
@InstallIn(SingletonComponent::class)
object NetworkModule {
    @Provides
    @Singleton
    fun provideOkHttpClient(): OkHttpClient =
        OkHttpClient.Builder()
            .addInterceptor(HttpLoggingInterceptor().apply { level = Level.BODY })
            .connectTimeout(30, TimeUnit.SECONDS)
            .build()

    @Provides
    @Singleton
    fun provideRetrofit(okHttpClient: OkHttpClient): Retrofit =
        Retrofit.Builder()
            .baseUrl(BuildConfig.BASE_URL)
            .client(okHttpClient)
            .addConverterFactory(Json.asConverterFactory("application/json".toMediaType()))
            .build()

    @Provides
    @Singleton
    fun provideItemApiService(retrofit: Retrofit): ItemApiService =
        retrofit.create(ItemApiService::class.java)
}

Rule 3: Scope correctly

// @Singleton — one instance for app lifetime
@Binds @Singleton abstract fun bindRepo(impl: RepoImpl): Repo

// @ActivityRetainedScoped — survives rotation, dies with activity backstack
// (default for @HiltViewModel — don't re-declare it)

// @ViewModelScoped — tied to ViewModel lifetime
@Binds @ViewModelScoped abstract fun bindSomeHelper(impl: SomeHelperImpl): SomeHelper

// @ActivityScoped — tied to Activity lifetime (rare)
@Binds @ActivityScoped abstract fun bindNavigator(impl: NavigatorImpl): Navigator

// ❌ Injecting @Singleton into @ViewModelScoped — scope violation, Dagger error

Rule 4: @HiltViewModel — correct pattern

// ✅ ViewModel injection
@HiltViewModel
class HomeViewModel @Inject constructor(
    private val getItems: GetItemsUseCase,
    private val savedStateHandle: SavedStateHandle  // free with Hilt
) : ViewModel() { ... }

// ✅ In Composable — always hiltViewModel(), never viewModel()
@Composable
fun HomeScreen(viewModel: HomeViewModel = hiltViewModel()) { ... }

// ✅ Scoped to NavGraph entry
@Composable
fun CheckoutFlow(navController: NavController) {
    val parentEntry = remember(navController) {
        navController.getBackStackEntry(CheckoutRoute)
    }
    val viewModel: CheckoutViewModel = hiltViewModel(parentEntry)
}

Rule 5: @AndroidEntryPoint on all Android classes that inject

// ✅ Required on: Activity, Fragment, View, Service, BroadcastReceiver
@AndroidEntryPoint
class MainActivity : ComponentActivity() {
    @Inject lateinit var analyticsTracker: AnalyticsTracker
}

@AndroidEntryPoint
class NotificationService : Service() {
    @Inject lateinit var notificationHandler: NotificationHandler
}

// ❌ Forgetting @AndroidEntryPoint — results in lateinit not initialized crash
class MainActivity : ComponentActivity() {
    @Inject lateinit var analyticsTracker: AnalyticsTracker  // CRASH: not injected
}

Rule 6: @EntryPoint for non-Hilt classes

// ✅ Access Hilt graph from classes Hilt doesn't manage
@EntryPoint
@InstallIn(SingletonComponent::class)
interface AnalyticsEntryPoint {
    fun analyticsTracker(): AnalyticsTracker
}

// Usage in a ContentProvider or non-Hilt class
val entryPoint = EntryPointAccessors.fromApplication(
    context.applicationContext,
    AnalyticsEntryPoint::class.java
)
val tracker = entryPoint.analyticsTracker()

Rule 7: @AssistedInject for runtime parameters

// ✅ When ViewModel needs a runtime value (e.g., item ID from nav args)
@HiltViewModel(assistedFactory = DetailViewModel.Factory::class)
class DetailViewModel @AssistedInject constructor(
    @Assisted val itemId: String,
    private val getItem: GetItemUseCase
) : ViewModel() {

    @AssistedFactory
    interface Factory {
        fun create(itemId: String): DetailViewModel
    }
}

// In Composable
@Composable
fun DetailScreen(itemId: String) {
    val viewModel: DetailViewModel = hiltViewModel<DetailViewModel, DetailViewModel.Factory>(
        creationCallback = { factory -> factory.create(itemId) }
    )
}

Rule 8: Qualifier for multiple bindings of same type

// ✅ @Qualifier to distinguish between two instances of same type
@Qualifier
@Retention(AnnotationRetention.BINARY)
annotation class IoDispatcher

@Qualifier
@Retention(AnnotationRetention.BINARY)
annotation class MainDispatcher

@Module
@InstallIn(SingletonComponent::class)
object DispatchersModule {
    @Provides @IoDispatcher
    fun provideIoDispatcher(): CoroutineDispatcher = Dispatchers.IO

    @Provides @MainDispatcher
    fun provideMainDispatcher(): CoroutineDispatcher = Dispatchers.Main
}

// Usage
@HiltViewModel
class MyViewModel @Inject constructor(
    @IoDispatcher private val ioDispatcher: CoroutineDispatcher
) : ViewModel()

Rule 9: Testing with Hilt

// ✅ @HiltAndroidTest for integration tests
@HiltAndroidTest
class HomeViewModelTest {
    @get:Rule val hiltRule = HiltAndroidRule(this)

    @Inject lateinit var getItems: GetItemsUseCase

    @Before
    fun setUp() { hiltRule.inject() }

    @Test
    fun `loads items successfully`() = runTest {
        val viewModel = HomeViewModel(getItems)
        viewModel.uiState.test {
            assertEquals(HomeUiState.Loading, awaitItem())
            assertTrue(awaitItem() is HomeUiState.Success)
        }
    }
}

// ✅ Replace production bindings in tests
@TestInstallIn(components = [SingletonComponent::class], replaces = [RepositoryModule::class])
@Module
abstract class FakeRepositoryModule {
    @Binds @Singleton
    abstract fun bindItemRepository(impl: FakeItemRepository): ItemRepository
}

Rule 10: Common Dagger compile errors and fixes

ErrorCauseFix
[Dagger/MissingBinding]Missing @Provides / @BindsAdd module with binding
[Dagger/IncompatiblyScopedBindings]Singleton injected into narrower scopeMatch scopes or remove scope annotation
abstract @Provides@Provides in abstract classUse object for @Provides, abstract class for @Binds
@Binds methods must have only one parameterWrong @Binds signatureabstract fun bind(impl: Impl): Interface
lateinit var not initializedMissing @AndroidEntryPointAdd @AndroidEntryPoint to class
Cannot be provided without @Inject or @ProvidesInterface binding missingAdd @Binds module

Deep-dive references

  • references/hilt-testing.md — full test setup with @TestInstallIn and fake modules
  • references/hilt-workmanager.md — @HiltWorker and WorkManager integration

What ships with it: 13 files

43.5 KB alongside SKILL.md

references/

Keep looking

Skills are one crate of 325,949. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.