K
oin is a DSL(Domain Specific Language), a light container and a pragmatic API, using Kotlin’s DSLs, it describes your dependencies into modules and sub-modules. It helps you describe your definitions with certain key functions that mean something in the Koin context.
Key Concepts for Koin
With Koin, setting up dependency injection is kind of a manual process, you essentially plug your services into the module graphs and those dependencies will later get resolved at runtime. Here are the key functions.
module {}
Creates a Koin Module or a submodule (inside a module).
factory {}
Provides a factory bean definition. It is used to indicate that a new instance must be created each time it is injected.
single {}
Provides a bean definition. It indicates that a unique instance will be created at the beginning and shared on every future injection.
get()
Resolves a component dependency.It is used inside constructors to resolve needed instances.
bind
Additional Kotlin type binding for a given bean definition
getProperty()
Resolves a property
So, How to implement it?
First, get the library into your project. You will need to add the following code to your module-level “build.gradle”.
// Add this to repositories if needed
repositories {
jcenter()
}
dependencies {
// koin version
def koin_version = '1.0.2'
// Koin for Android
implementation "org.koin:koin-android:$koin_version"
// or Koin for view model features
implementation "org.koin:koin-android-viewmodel:$koin_version"
}
Modules in Koin
- The module definition is very similar to Dagger-2 @Module definitions, it serves as a container for a collection of services that should be provided at runtime. You put all the services that a module should provide in the module block.
- The other thing is that the module block is assigned as a value, this is because the Koin framework uses a list of modules to build the dependency graph
- For Example: If we take an example of some structure like MVVM then we would make a different module for different modules in our project like a repository, viewModel, database, network, etc.
val viewModelModule = module {
//Provides an instance of ViewModel and binds it to an Android Component lifecycle
viewModel { LauncherViewModel(get()) }
}
val repositoryModule = module {
//Provides a singleton instance
single { LoginStatusRepository(get()) }
}
val interactorModule = module {
//Provides a new instance for each call
factory { LoginStatusInteractor() }
}
Single
The single definition does the exact opposite of what the factory does. It tells Koin to retain the instance and provide that single instance wherever needed. This can be done in some ways similar to the factory defined above. It is similar to Dagger-2 @Singleton annotation.
Example:
val testModule = module {
// create one instance to be used
single { Date() }
// then you can automatically resolve SomeClass
singleSomeClass()
// or you can use a 'single' block to compose the service
single {
val currentDate = Date()
return@single SomeClass(currentDate)
}
}
Factory
A factory serves the purpose of qualifying a dependency, it tells the Koin framework not to retain this instance but rather, create a new instance of this service anywhere the service is required. This can be done in a couple of ways:
Example:
interface SomeInterface {
fun getSimpleDate(): String
}
class SomeClass(val date: Date): SomeInterface {
override fun getSimpleDate(): String {
return SimpleDateFormat.getDateInstance().format(date)
}
}
val testModule = module {
// then you can automatically resolve SomeClass
factory()
}
How to add dependencies in the Main Application class?
- After declaring all the modules and resolving dependencies, we need to start the koin function in order to add dependencies at run time.
- Remember that all the dependencies will be resolved at runtime.
- The startKoin function is the main entry point to launch Koin container. It needs a list of Koin modules to run. Modules are loaded and definitions are ready to be resolved by the Koin container.
Example:
class MyApplication : Application() {
override fun onCreate() {
startKoin(this, listOf(viewModelModule, repositoryModule), interactorModule)
}
}
Manually Injecting it!!
Sometimes you can’t declare only components via Koin. Depending on your runtime technology, you will need to retrieve instances from Koin in a class that is not created with Koin (i.e: Android)
Tag your class with the KoinComponent interface to unlock Koin injection features:
by inject()
Lazy inject an instance
get()
Retrieve an instance
getProperty()
Get a Koin property
Injecting is also crazy easy in Koin. Resolving a dependency in a lazy fashion is as easy as using inject().
We can inject the lazy variables manually also if they are initialized by late in kotlin.
Example:
val loginStatusInteractor: LoginStatusInteractor by inject()
val launcherViewModel: LauncherViewModel by viewModel()
Now, Scope
- A scope is a fixed duration of time in which an object exists. When the scope context ends, any objects bound under that scope cannot be injected again (they are dropped from the container).
- By default in Koin, we have 3 kinds of scopes:
- single definition, create an object that persistent with the entire container lifetime (can’t be dropped).
- factory definition, create a new object each time. No persistence in the container (can’t be shared).
- scoped definition, create an object that persistent tied to the associated scope lifetime.
Example:
// given the classes
class ComponentA
class ComponentB(val a : ComponentA)
// module with scope
module {
scope(named("A_SCOPE_NAME")){
scoped { ComponentA() }
// will resolve from current scope instance
scoped { ComponentB(get()) }
}
}
Closing Scope
Once your scope instance is finished, just closed it with the close() function
Example:
// from a KoinComponent
val session = getKoin().createScope("session")
// use it ...
// close it
session.close()
Load and Unload Modules of Koin
Loading modules after startKoin
- You can’t call the startKoin function more than once. But you can use directly the loadKoinModules() functions.
- This function is interesting for SDK makers who want to use Koin, because they don’t need to use the starKoin() function and just use the loadKoinModules at the start of their library.
Example: loadKoinModules(module1,module2 …)
UnLoading modules after startKoin
it’s possible also to unload a bunch of definitions and then release their instance with the given function.
Example: unloadKoinModules(module1,module2 …)
Stop Koin
You can close all the Koin resources and drop instances & definitions. For this you can use the stopKoin() function from anywhere, to stop the Koin GlobalContext. Else on a KoinApplication instance, just call close().