Proprietary & Confidential
Dependency
injection with Koin
2019.1.17
Sean Tsai
sean.tsai@honestbee.com
Proprietary & Confidential
A pragmatic lightweight dependency injection framework for Kotlin
developers. Written in pure Kotlin using functional resolution only:
no proxy, no code generation, no reflection!
Koin is a DSL, a lightweight container and a pragmatic API.
What’s Koin?
Proprietary & Confidential
Getting Started
Proprietary & Confidential
// Add Jcenter to your repositories if needed
repositories {
jcenter ()
}
dependencies {
// Koin for Android
compile 'org.koin:koin-android:1.0.2'
// Koin for Java developers
// implementation "org.koin:koin-java:1.0.2"
// Koin for Unit tests
// testImplementation "org.koin:koin-test:1.0.2"
}
Grade Setup
Proprietary & Confidential
interface HelloRepository {
fun giveHello(): String
}
class HelloRepositoryImpl () : HelloRepository {
override fun giveHello() = "Hello Koin"
}
Our components - Repository
Proprietary & Confidential
class MySimplePresenter (val repo: HelloRepository) {
fun sayHello() = "${repo.giveHello()} from $this"
}
Our components - Presenter
Proprietary & Confidential
val appModule = module {
// single instance of HelloRepository
single<HelloRepository> { HelloRepositoryImpl() }
// Simple Presenter Factory
factory { MySimplePresenter( get()) }
}
Writing the Koin module
Proprietary & Confidential
class MyApplication : Application(){
override fun onCreate() {
super.onCreate()
// Start Koin
startKoin( this, listOf(appModule))
}
}
Start Koin
Proprietary & Confidential
class MySimpleActivity : AppCompatActivity() {
// Lazy injected MySimplePresenter
val firstPresenter: MySimplePresenter by inject()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
//...
}
}
Injecting dependencies
Proprietary & Confidential
More Details
Proprietary & Confidential
● module - create a Koin Module
● factory - provide a factory bean definition
● single - provide a singleton bean definition (also aliased as bean)
● get - resolve a component dependency
● bind - add type to bind for given bean definition
The Koin DSL
Proprietary & Confidential
val appModule = module {
// single instance of HelloRepository
single<HelloRepository> { HelloRepositoryImpl() }
// Simple Presenter Factory
factory { MySimplePresenter( get()) }
}
Let’s get back to previous example....
Proprietary & Confidential
Experimental features - create<T>()
// Presenter <- Service
class Service()
class Controller(val view : View)
val myModule = module {
// declare Service as single instance
single { Service() }
// declare Controller as single instance, resolving View instance
with get()
single<Controller> { ControllerImpl(get()) }
}
Proprietary & Confidential
Experimental features - create<T>()
// Presenter <- Service
class Service()
class Controller(val view : View)
val myModule = module {
// declare Service as single instance
single<Service>()
// declare Controller as single instance, resolving View instance
with get()
single<Controller> { create<ControllerImpl>() }
}
Proprietary & Confidential
val myModule = module {
// Will match type ServiceImp only
single { ServiceImp() }
// Will match type Service only
single { ServiceImp() as Service }
}
Binding an interface
● A single or a factory definition use the type from the their given lambda definition: i.e single {
T } The matched type of the definition is the only matched type from this expression.
Proprietary & Confidential
val myModule = module {
// Will match type ServiceImp only
single { ServiceImp() }
// Will match type Service only
single<Service> { ServiceImp() }
}
Binding an interface
Proprietary & Confidential
val myModule = module {
// Will match types ServiceImp & Service
single { ServiceImp() } bind Service::class
}
Binding additional type
Proprietary & Confidential
Declaring injection parameters
class Presenter(val view : View)
val myModule = module {
single{ (view : View) -> Presenter(view) }
}
val presenter : Presenter by inject { parametersOf(view) }
Proprietary & Confidential
Module - Naming a definition
module {
single { ComponentA() }
module("A") {
single { ComponentB() }
}
}
ComponentA ->
ComponentB -> A.ComponentB
Proprietary & Confidential
Module - Naming a definition
val myModule = module {
single<Service>("default") { ServiceImpl() }
single<Service>("test") { ServiceImpl() }
}
val service : Service by inject(name = "default")
Proprietary & Confidential
Module - Visibility rules
● Visibility rule is quite simple: child modules can see their parents, but not the inverse.
A definition from a child module, can see definitions in parents modules. Modules can’t
share their definitions in divergent paths.
Proprietary & Confidential
Module - Visibility rules
// definitions in /
val rootModule = module {
single { ComponentA() }
}
// definitions in /org
val orgModule = module("org") {
single { ComponentB(...) }
}
// definitions in /org/sample
val sampleModule = module("org.sample") {
single { ComponentC(...) }
}
// definitions in /org/demo
val demoModule = module("org.demo") {
single { ComponentD(...) }
}
Proprietary & Confidential
Module - Visibility rules
// definitions in /
val rootModule = module {
single { ComponentA() }
}
// definitions in /org
val orgModule = module("org") {
single { ComponentB(...) }
}
// definitions in /org/sample
val sampleModule = module("org.sample") {
single { ComponentC(...) }
}
// definitions in /org/demo
val demoModule = module("org.demo") {
single { ComponentD(...) }
}
Can only see:
➢ /
Proprietary & Confidential
Module - Visibility rules
// definitions in /
val rootModule = module {
single { ComponentA() }
}
// definitions in /org
val orgModule = module("org") {
single { ComponentB(...) }
}
// definitions in /org/sample
val sampleModule = module("org.sample") {
single { ComponentC(...) }
}
// definitions in /org/demo
val demoModule = module("org.demo") {
single { ComponentD(...) }
}
Can only see:
➢ /
➢ /org
Proprietary & Confidential
Module - Visibility rules
// definitions in /
val rootModule = module {
single { ComponentA() }
}
// definitions in /org
val orgModule = module("org") {
single { ComponentB(...) }
}
// definitions in /org/sample
val sampleModule = module("org.sample") {
single { ComponentC(...) }
}
// definitions in /org/demo
val demoModule = module("org.demo") {
single { ComponentD(...) }
}
Can only see:
➢ /
➢ /org
➢ /org/sample
Proprietary & Confidential
Module - Visibility rules
// definitions in /
val rootModule = module {
single { ComponentA() }
}
// definitions in /org
val orgModule = module("org") {
single { ComponentB(...) }
}
// definitions in /org/sample
val sampleModule = module("org.sample") {
single { ComponentC(...) }
}
// definitions in /org/demo
val demoModule = module("org.demo") {
single { ComponentD(...) }
}
Can only see:
➢ /
➢ /org
➢ /org/demo
Proprietary & Confidential
If you do care about your modules
lifetime. Can check with Scope section.
Proprietary & Confidential
Reference
• Koin Official Site
• Koin Official Doc
Proprietary & Confidential
Q & A

Dependency injection with koin

  • 1.
    Proprietary & Confidential Dependency injectionwith Koin 2019.1.17 Sean Tsai sean.tsai@honestbee.com
  • 2.
    Proprietary & Confidential Apragmatic lightweight dependency injection framework for Kotlin developers. Written in pure Kotlin using functional resolution only: no proxy, no code generation, no reflection! Koin is a DSL, a lightweight container and a pragmatic API. What’s Koin?
  • 3.
  • 4.
    Proprietary & Confidential //Add Jcenter to your repositories if needed repositories { jcenter () } dependencies { // Koin for Android compile 'org.koin:koin-android:1.0.2' // Koin for Java developers // implementation "org.koin:koin-java:1.0.2" // Koin for Unit tests // testImplementation "org.koin:koin-test:1.0.2" } Grade Setup
  • 5.
    Proprietary & Confidential interfaceHelloRepository { fun giveHello(): String } class HelloRepositoryImpl () : HelloRepository { override fun giveHello() = "Hello Koin" } Our components - Repository
  • 6.
    Proprietary & Confidential classMySimplePresenter (val repo: HelloRepository) { fun sayHello() = "${repo.giveHello()} from $this" } Our components - Presenter
  • 7.
    Proprietary & Confidential valappModule = module { // single instance of HelloRepository single<HelloRepository> { HelloRepositoryImpl() } // Simple Presenter Factory factory { MySimplePresenter( get()) } } Writing the Koin module
  • 8.
    Proprietary & Confidential classMyApplication : Application(){ override fun onCreate() { super.onCreate() // Start Koin startKoin( this, listOf(appModule)) } } Start Koin
  • 9.
    Proprietary & Confidential classMySimpleActivity : AppCompatActivity() { // Lazy injected MySimplePresenter val firstPresenter: MySimplePresenter by inject() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) //... } } Injecting dependencies
  • 10.
  • 11.
    Proprietary & Confidential ●module - create a Koin Module ● factory - provide a factory bean definition ● single - provide a singleton bean definition (also aliased as bean) ● get - resolve a component dependency ● bind - add type to bind for given bean definition The Koin DSL
  • 12.
    Proprietary & Confidential valappModule = module { // single instance of HelloRepository single<HelloRepository> { HelloRepositoryImpl() } // Simple Presenter Factory factory { MySimplePresenter( get()) } } Let’s get back to previous example....
  • 13.
    Proprietary & Confidential Experimentalfeatures - create<T>() // Presenter <- Service class Service() class Controller(val view : View) val myModule = module { // declare Service as single instance single { Service() } // declare Controller as single instance, resolving View instance with get() single<Controller> { ControllerImpl(get()) } }
  • 14.
    Proprietary & Confidential Experimentalfeatures - create<T>() // Presenter <- Service class Service() class Controller(val view : View) val myModule = module { // declare Service as single instance single<Service>() // declare Controller as single instance, resolving View instance with get() single<Controller> { create<ControllerImpl>() } }
  • 15.
    Proprietary & Confidential valmyModule = module { // Will match type ServiceImp only single { ServiceImp() } // Will match type Service only single { ServiceImp() as Service } } Binding an interface ● A single or a factory definition use the type from the their given lambda definition: i.e single { T } The matched type of the definition is the only matched type from this expression.
  • 16.
    Proprietary & Confidential valmyModule = module { // Will match type ServiceImp only single { ServiceImp() } // Will match type Service only single<Service> { ServiceImp() } } Binding an interface
  • 17.
    Proprietary & Confidential valmyModule = module { // Will match types ServiceImp & Service single { ServiceImp() } bind Service::class } Binding additional type
  • 18.
    Proprietary & Confidential Declaringinjection parameters class Presenter(val view : View) val myModule = module { single{ (view : View) -> Presenter(view) } } val presenter : Presenter by inject { parametersOf(view) }
  • 19.
    Proprietary & Confidential Module- Naming a definition module { single { ComponentA() } module("A") { single { ComponentB() } } } ComponentA -> ComponentB -> A.ComponentB
  • 20.
    Proprietary & Confidential Module- Naming a definition val myModule = module { single<Service>("default") { ServiceImpl() } single<Service>("test") { ServiceImpl() } } val service : Service by inject(name = "default")
  • 21.
    Proprietary & Confidential Module- Visibility rules ● Visibility rule is quite simple: child modules can see their parents, but not the inverse. A definition from a child module, can see definitions in parents modules. Modules can’t share their definitions in divergent paths.
  • 22.
    Proprietary & Confidential Module- Visibility rules // definitions in / val rootModule = module { single { ComponentA() } } // definitions in /org val orgModule = module("org") { single { ComponentB(...) } } // definitions in /org/sample val sampleModule = module("org.sample") { single { ComponentC(...) } } // definitions in /org/demo val demoModule = module("org.demo") { single { ComponentD(...) } }
  • 23.
    Proprietary & Confidential Module- Visibility rules // definitions in / val rootModule = module { single { ComponentA() } } // definitions in /org val orgModule = module("org") { single { ComponentB(...) } } // definitions in /org/sample val sampleModule = module("org.sample") { single { ComponentC(...) } } // definitions in /org/demo val demoModule = module("org.demo") { single { ComponentD(...) } } Can only see: ➢ /
  • 24.
    Proprietary & Confidential Module- Visibility rules // definitions in / val rootModule = module { single { ComponentA() } } // definitions in /org val orgModule = module("org") { single { ComponentB(...) } } // definitions in /org/sample val sampleModule = module("org.sample") { single { ComponentC(...) } } // definitions in /org/demo val demoModule = module("org.demo") { single { ComponentD(...) } } Can only see: ➢ / ➢ /org
  • 25.
    Proprietary & Confidential Module- Visibility rules // definitions in / val rootModule = module { single { ComponentA() } } // definitions in /org val orgModule = module("org") { single { ComponentB(...) } } // definitions in /org/sample val sampleModule = module("org.sample") { single { ComponentC(...) } } // definitions in /org/demo val demoModule = module("org.demo") { single { ComponentD(...) } } Can only see: ➢ / ➢ /org ➢ /org/sample
  • 26.
    Proprietary & Confidential Module- Visibility rules // definitions in / val rootModule = module { single { ComponentA() } } // definitions in /org val orgModule = module("org") { single { ComponentB(...) } } // definitions in /org/sample val sampleModule = module("org.sample") { single { ComponentC(...) } } // definitions in /org/demo val demoModule = module("org.demo") { single { ComponentD(...) } } Can only see: ➢ / ➢ /org ➢ /org/demo
  • 27.
    Proprietary & Confidential Ifyou do care about your modules lifetime. Can check with Scope section.
  • 28.
    Proprietary & Confidential Reference •Koin Official Site • Koin Official Doc
  • 29.