What is Koin?
A programmatic, lightweight dependency injection framework for Kotlin developers Written in pure Kotlin
What are dependency and dependency injection?
Let’s take an example. We have a relationship between Car and Engine classes. To access Engine’s properties in the Car class, we have to create an instance of Engine Car that is simply a dependency.
Instance of the engine class can be created manually. but it becomes hard to instantiate the objects automatically, such as in the constructor.
Asking someone else to create the object and directly using it is called dependency injection.
Let’s add Koin to our project!
// Koin AndroidX Scope features
implementation "io.insert-koin:koin-androidx-scope:2.2.3"
// Koin AndroidX ViewModel features
implementation "io.insert-koin:koin-androidx-viewmodel:2.2.3"
// Koin AndroidX Fragment features
implementation "io.insert-koin:koin-androidx-fragment:2.2.3"
// Koin AndroidX WorkManager
// implementation "io.insert-koin:koin-androidx-workmanager:$koin_version"
// Koin AndroidX Jetpack Compose
// implementation "io.insert-koin:koin-androidx-compose:$koin_version"
// Koin AndroidX Experimental features
// implementation "io.insert-koin:koin-androidx-ext:$koin_version"}
let’s add this koin plugin in build.gradle(app).
plugins {
id 'com.android.application'
id 'kotlin-android'
id 'koin'
}
Now first create our BaseApplication class like
class BaseApplication:Application()
add this BaseApplication application tag of manifest file like,
android:name=".BaseApplication"
for Koin we have three types of Scopes
, we can define it as:
single:
it creates a singleton that can be used across the app as a singular instance.factory :
, it provides a bean definition, which will create a new instance each time it is injected.scoped:
, it’s provide an object that will persist as long the associated scope lifetime exist
Constructor injection.
“constructor injection” refers to a type of dependency injection where dependencies are injected through the constructor of a class.
class Car constructor(private val engine: Engine, private val wheel: Wheel){
companion object{
const val TAG ="Car"
}
fun getCar(){
wheel.getWheel()
engine.getEngine()
Log.d(TAG, "getCar: Car is Running")
}
}
class Engine {
companion object{
const val TAG ="Engine"
}
fun getEngine(){
Log.d(TAG, "Engine started")
}
}
class Wheel {
companion object{
const val TAG ="Wheel"
}
fun getWheel(){
Log.d(TAG, "Wheel started")
}
}
Now declare your dependencies using the module
DSL in Koin. In the module, you specify how to create instances of your classes and their dependencies.
We will keep logic in module to provide dependency like (interface and class) to provide car class Dependency create a module AppModule file.
val demoModule = module {
single {
Wheel()
}
single {
Engine()
}
// every time inject new instance created if used factory scope
// get() to inject wheel and engine dependency
single {
Car(get(),get())
}
single {
Component()
}
}
Implementing Koin Component
The KoinComponent
interface is used to enable dependency injection features in a class. It provides access to the Koin instance, allowing the class to retrieve dependencies using the by inject()
property delegate. Here’s an example:
@KoinApiExtension
class Component() :KoinComponent{
// lazily instantiated (when first access)
val car: Car by inject()
val engine:Engine by inject ()
// also another way
// eagerly // when application created will be available
val car1:Car = get()
// val main: Main by inject()
//val mainViewModel:MainViewModel by inject ()
}
Now we have declared all modules (dependencies). In Application class, just call startKoin{ } method that takes a lambda in which we define a list of modules
class BaseApplication:Application() {
override fun onCreate() {
super.onCreate()
// first start Koin(to use)
startKoin {
//can keep list of modules
modules(listOf(demoModule,))
// modules(
// demoModule
// )
}
}
}
Let’s inject in MainActivity, first use @KoinApiExtension in activity,fragment etc.
@KoinApiExtension
class MainActivity : AppCompatActivity() {
var binding:ActivityMainBinding? = null
private val component:Component by inject()
// private val car: Car by inject() /// not a better way create component than use
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
binding?.apply {
setContentView(root)
lifecycleOwner = this@MainActivity
executePendingBindings()
}
component.car.getCar()
}
}
check this sample project https://github.com/sanjaydraws/Koin-sample