DI.
Koin
Author:Semen Naduiev
What is Dependency Injection (DI)
A design pattern used to reduce coupling between classes by injecting their dependencies from
the outside rather than creating them inside the class.
Why Use DI?
✅ Improves testability
✅ Encourages modular code
✅ Reduces boilerplate
✅ Promotes Single Responsibility Principle
✅ Makes code easier to maintain
Types of Dependency Injection
1. Constructor Injection
class Car(val engine: Engine)
2. Field Injection (used with libraries like Dagger/Hilt)
@Inject lateinit var engine: Engine
3. Setter Injection
class Car {
fun setEngine(engine: Engine) { ... }
}
DI Frameworks in Android
Hilt (recommended by Google) ✅
Dagger 2 (powerful but complex)
Koin (lightweight, Kotlin-friendly)
Kodein (modular)
What is Inversion of Control (IoC)?
Inversion of Control (IoC) is a design principle in which control over program flow is inverted —
instead of a class controlling its dependencies, the control is handed over to a framework or
external code.
Traditional vs IoC Example
How IoC is Applied
Through Dependency Injection
Through Service Locator Pattern
Through Event-Driven Architecture
Using Frameworks (like Hilt, Spring, etc.
IoC vs DI
Service Locator vs Code Generation
Service Locator — Design Pattern
The Service Locator is a design pattern that provides dependencies by accessing a centralized registry or
"locator".
📌 How it works:
A class requests its own dependencies from a global service.
Dependency creation and lifecycle are managed by the locator.
Service Locator
✅ Pros:
● Simple to implement.
● Centralized place to manage dependencies.
❌ Cons:
● Strong reliance on global state.
● Difficult to unit test.
● Breaks Inversion of Control (IoC) — the class pulls the dependency instead of having it
injected.
Code Generation — Technique (used in
Hilt/Dagger)
Code Generation is an automated approach where tools like Hilt or Dagger generate code to
handle dependency creation and injection.
📌 How it works:
You annotate classes with @Inject, @Module, @Provides, etc.
The framework builds a dependency graph at compile time.
You don’t create dependencies manually — the framework injects them.
Code Generation
Pros:
Full support for IoC — objects don’t create their own dependencies.
Easy to test (you can swap dependencies).
No global state.
❌ Cons:
Learning curve.
Harder to debug sometimes.
Slower build times in large projects.
Let’s Practice!
I will show you a simple service locator for our https://github.com/NameInTheSand/RoomDb
RoomDb project.
You can find it in the ServiceLocator branch
What is Koin?
Koin is a lightweight dependency injection framework for Kotlin.
Developed purely in Kotlin.
No code generation, no proxies.
Why use Koin?
Makes your code more modular and testable.
Avoids manual instantiation of objects.
Easy to integrate into Android apps.
Basic Terminology
Module – container of definitions
Definition – how a class should be created
Scope – lifespan of the object
Injection – how dependencies are provided
Defining a Module
Starting Koin in Android
Don’t forget to add android:name to your manifest file!
Injecting in ViewModel
Injecting in Composables or Activities
val viewModel: MyViewModel = koinViewModel()
Core Koin Definition Types
Additional Features
bind<Type>()
Provides a type alias for the object so it can be injected as another interface.
override = true
Overrides an existing definition — useful for testing.
includes(...)
Combines multiple modules.
Injecting ViewModel in Android
private val viewModel: UserViewModel by viewModel()
Injecting ViewModel in Compose
Gradle Setup
implementation ("io.insert-koin:koin-android: $koin_version ")
// Koin for Kotlin apps
implementation( "io.insert-koin:koin-core:$koin_version")
Koin vs Dagger/Hilt (Comparison)
Summary
Koin is great for small to medium Android apps.
Easy to learn and quick to set up.
Perfect for beginners and student projects.