Koin — Dependency Injection Container for Kotlin
In modern Kotlin development, dependency management is an important task. Koin is a lightweight Dependency Injection (DI) container written in Kotlin that requires no code generation, reflection, or complex configuration. Unlike Dagger or Spring, Koin uses a simple DSL and fully integrates with coroutines and Flow. On this page, we will cover how to install Koin, declare modules, inject dependencies, and use it in educational projects.
What is Dependency Injection?
Dependency Injection (DI) is a pattern where an object receives its dependencies from the outside rather than creating them itself. This simplifies testing, makes code more flexible, and reduces coupling. Koin implements DI through a container that manages the creation and lifecycle of objects.
Installing Koin
Add the following dependency to the build.gradle.kts file:
// build.gradle.ktsdependencies { implementation("io.insert-koin:koin-core:3.5.3") // For Android implementation("io.insert-koin:koin-android:3.5.3")}Core Concepts of Koin
- Module — a block where dependencies are declared.
- Definition — the way an object is created (single, factory, scoped).
- Container (KoinApplication) — starts and manages modules.
- Injection (inject) — obtaining a dependency via delegates or functions.
Creating Modules and Declaring Dependencies
Koin uses DSL to describe modules. Consider an example with a repository and a service:
// Define interface and implementationinterface UserRepository { fun getUser(id: Int): String}
class UserRepositoryImpl : UserRepository { override fun getUser(id: Int) = "User #$id"}
class UserService(private val repository: UserRepository) { fun printUser(id: Int) { println(repository.getUser(id)) }}
// Koin moduleval appModule = module { // single — creates one instance (singleton) single<UserRepository> { UserRepositoryImpl() } // factory — creates a new instance on each request factory { UserService(get()) }}Starting the Container
Before using dependencies, you need to start Koin:
import org.koin.core.context.startKoin
fun main() { startKoin { modules(appModule) } // Now you can obtain dependencies val service: UserService by inject() service.printUser(42)}Ways to Inject Dependencies
- by inject() — lazy retrieval (recommended).
- get() — immediate retrieval.
- Constructor — Koin automatically passes dependencies to the constructor.
Example using by inject():
class MyActivity : AppCompatActivity() { // Lazy injection private val repository: UserRepository by inject() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) // Use repository }}Scoped Dependencies
Sometimes you need to create a dependency that lives within a specific context (e.g., a user session). For this, use scope:
val sessionModule = module { scope<UserSession> { scoped { UserSessionManager(get()) } }}Working with Coroutines
Koin supports coroutines via the koin-core-coroutines module. Example of asynchronous retrieval:
import org.koin.core.component.KoinComponentimport org.koin.core.component.inject