360 Secure SkillHub
360 安全技能中心

android-di-koin

该技能给出 Android/KMP 使用 Koin 的依赖注入实践:按数据层和展示层定义模块,在 :app 中通过 startKoin 装配,并在 Compose Root 中用 koinViewModel() 注入 ViewModel,同时包含作用域、命名约定与新增功能检查清单。

分类:开发
hereisderek
热度 0.00
最近更新 2026-10-21 23:57:01

安装

发给 Agent
帮我安装这个技能:npx secure-skills-cli add hereisderek/derek-agent-skills/android-di-koin
兼容性
通用兼容
联网要求
不联网
数据出网
数据不出本机
Metadata
name
android-di-koin
description
Koin dependency injection setup for Android/KMP - module definitions per layer, ViewModel injection, assembling modules in :app, and koinViewModel() in composables. Use this skill whenever setting up Koin, defining a DI module, providing a repository or ViewModel, injecting a dependency, or wiring modules in the Application class. Trigger on phrases like "set up Koin", "add a Koin module", "inject a dependency", "DI module", "koinViewModel", "provide a ViewModel", "startKoin", or "single/viewModel/factory".

Android / KMP Dependency Injection (Koin)

Principles

  • One Koin module per feature layer — create it only if there are dependencies to provide.
  • Modules are assembled in :app, never in feature modules themselves.
  • In Compose root composables, always inject ViewModels via koinViewModel().

Module Definitions

Prefer the constructor-reference overloads (singleOf, viewModelOf, factoryOf) — they are more concise and let Koin resolve parameters automatically. Only fall back to the lambda overloads (single { ... }, viewModel { ... }, factory { ... }) when constructor injection alone is not enough, e.g. when you need to call a factory method, pass a named/qualified dependency, or do post-construction setup.

Data layer module

// feature:notes:data
val notesDataModule = module {
    singleOf(::RoomNoteRepository) { bind<NoteRepository>() }
}

Presentation layer module

// feature:notes:presentation
val notesPresentationModule = module {
    viewModelOf(::NoteListViewModel)
    viewModelOf(::NoteDetailViewModel)
}

Core data module (example)

// core:data
val coreDataModule = module {
    // singleOf works when the class has a single injectable constructor
    singleOf(::SessionPreferences)

    // Lambda overload needed here — calling a factory method, not a constructor
    single { HttpClientFactory.create(get()) }
    single { createDataStore(get()) }
}

Assembly in :app

Register all modules in the Application class:

class App : Application() {
    override fun onCreate() {
        super.onCreate()
        startKoin {
            androidContext(this@App)
            modules(
                // core
                coreDataModule,
                // features
                notesDataModule,
                notesPresentationModule,
                authDataModule,
                authPresentationModule,
                // ...
            )
        }
    }
}

Injecting in Composables

Always use koinViewModel() in Root composables:

@Composable
fun NoteListRoot(
    onNavigateToDetail: (String) -> Unit,
    viewModel: NoteListViewModel = koinViewModel()
) { ... }

Never pass ViewModels down the composable tree — inject at the Root level only.


Scoping Rules

Scope Preferred form Fallback form When to use
Singleton singleOf(::Impl) { bind<Interface>() } single { ... } One instance for the app lifetime (repositories, HttpClient, DB)
ViewModel viewModelOf(::MyViewModel) viewModel { ... } ViewModel instances scoped to their lifecycle
Factory factoryOf(::Impl) factory { ... } New instance on every injection (rare — prefer singleton or ViewModel)

Use the *Of constructor-reference form by default. Only use the lambda form when you cannot express the binding with a constructor reference (factory methods, named qualifiers, manual setup).


Naming Conventions

Thing Convention Example
Koin module <feature><Layer>Module notesDataModule, notesPresentationModule

Worker DI

For WorkManager Worker injection, see android-background skill — covers KoinWorkerFactory setup, disabling the default initializer, and the worker { } scope.


Checklist: Adding DI for a New Feature

  • Define val <feature>DataModule = module { ... } in feature:data
  • Define val <feature>PresentationModule = module { ... } in feature:presentation
  • Register both modules in :app's startKoin { modules(...) }
  • Use koinViewModel() in all Root composables