Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Insert Koin

2aec47eb9a940c619f05972f0db5aa00?s=47 Kirill Rozov
November 13, 2018

Insert Koin

Koin is the next step to becoming a project only with Kotlin code. What inside Koin? Only DSL & Kotlin magic. It is possible to effectively replace Dagger 2? Let’s find out does Dependency Injection + Kotlin + Easy of Use == Koin?

2aec47eb9a940c619f05972f0db5aa00?s=128

Kirill Rozov

November 13, 2018
Tweet

Transcript

  1. Kirill Rozov Android Developer

  2. Dependency Injection

  3. Inversion of Control (IoC) is a design principle in which

    custom-written portions of a computer program receive the flow of control from a generic framework wikipedia.org/wiki/Inversion_of_control
  4. No IoC CLIENT CLIENT CLIENT DEPENDENCY 1 DEPENDENCY 2 DEPENDENCY

    3 DEPENDENCY 4
  5. With IoC DEPENDENCY 1 DEPENDENCY 2 DEPENDENCY 3 DEPENDENCY 4

    IoC CONTAINER CLIENT CLIENT CLIENT
  6. With IoC DEPENDENCY 1 DEPENDENCY 2 DEPENDENCY 3 DEPENDENCY 4

    IoC CONTAINER CLIENT CLIENT CLIENT
  7. Popular DI • Guice • Spring DI • Square Dagger

    • Google Dagger 2 • Java EE CDI • PicoContainer • Kodein • Koin
  8. insert-Koin.io

  9. None
  10. Sample

  11. Sample val sampleModule = module { single { ComponentA() }

    }
  12. Sample val sampleModule = module { single { ComponentA() }

    }
  13. Sample val sampleModule = module { single { ComponentA() }

    }
  14. Sample val sampleModule = module { single { ComponentA() }

    } class Application : KoinComponent { val component by inject<ComponentA>() }
  15. Sample val sampleModule = module { single { ComponentA() }

    } class Application : KoinComponent { val component by inject<ComponentA>() }
  16. Sample val sampleModule = module { single { ComponentA() }

    } class Application : KoinComponent { val component by inject<ComponentA>() }
  17. Sample val sampleModule = module { single { ComponentA() }

    } class Application : KoinComponent { val component by inject<ComponentA>() } fun main(vararg args: String) { startKoin(listOf(sampleModule)) }
  18. Sample class SampleApplication : Application() { override fun onCreate() {

    startKoin(listOf(sampleModule)) } }
  19. Provide dependencies module { single { ComponentA() } factory {

    ComponentB() } }
  20. Provide dependencies module { single { ComponentA() } factory {

    ComponentB() } }
  21. Provide dependencies module { single { ComponentA() } factory {

    ComponentB() } }
  22. Provide dependencies class ComponentA(c : ComponentB)

  23. Provide dependencies module { single { ComponentB() } single {

    ComponentA(get<ComponentB>()) } } class ComponentA(c : ComponentB)
  24. Dependency Parameters

  25. Properties class RestService(url: String)

  26. Properties module { factory { RestService(getProperty(“url”)) } } class RestService(url:

    String)
  27. Properties module { factory { RestService(getProperty(“url”)) } } class RestService(url:

    String) module { factory { RestService(getProperty(“url”, “http://localhost”)) } }
  28. Properties module { factory { RestService(getProperty(“url”)) } } class RestService(url:

    String) module { factory { RestService(getProperty(“url”, “http://localhost”)) } } val key1Property: String by property(“key1”)
  29. Properties Sources

  30. Properties Sources • On initialization

  31. Properties Sources • On initialization • Using KoinComponent.setProperty()

  32. Properties Sources • On initialization • Using KoinComponent.setProperty() • koin.properties

    in assets
  33. Properties Sources • On initialization • Using KoinComponent.setProperty() • koin.properties

    in assets • koin.properties in JAR resources
  34. Properties Sources • On initialization • Using KoinComponent.setProperty() • koin.properties

    in assets • koin.properties in JAR resources • Environment variables
  35. Problem class NewsDetailPresenter(newsId : String)

  36. Parameters val presenter: NewsDetailPresenter by inject { parametersOf(“sampleNewsId") }

  37. Parameters module { factory { (id : String) -> NewsDetailsPresenter(id)

    } } val presenter: NewsDetailPresenter by inject { parametersOf(“sampleNewsId") }
  38. Parameters module { factory { (id : String) -> NewsDetailsPresenter(id)

    } } val presenter: NewsDetailPresenter by inject { parametersOf(“sampleNewsId") }
  39. Names

  40. Duplicated dependencies module { single { ComponentB(…) } single {

    ComponentB(…) } }
  41. Named dependencies module { single(name = “debug”) { ComponentB(…) }

    single(name = “prod”) { ComponentB(…) } }
  42. Named dependencies class Application : KoinComponent { val componentProd by

    inject<ComponentB>(“prod”) val componentDebug by inject<ComponentB>(“debug”) } module { single(name = “debug”) { ComponentB(…) } single(name = “prod”) { ComponentB(…) } }
  43. Named dependencies module { single(name = “debug”) { ComponentB(…) }

    single { ComponentA(get(“debug”)) } }
  44. Namespaces

  45. Namespaces module { … }

  46. Namespaces module(path = “”) { … }

  47. Namespaces val rootModule = module { … } val sampleModule

    = module(path = “org.sample”) { … }
  48. Namespaces module { module(“org.sample”) { } }

  49. Namespaces module { module(“org.sample”) { } }

  50. Namespaces module(path = “org.sample”) { … } // is equals

    to module { module(“org”) { module(“sample”) { } } }
  51. Namespaces module { module("B") { factory { ComponentA() } factory

    { ComponentB(get()) } } module(“C") { factory { ComponentA() } factory { ComponentB(get()) } } } class ComponentB(c : ComponentA) class ComponentC(c : ComponentA)
  52. Namespaces module { module("B") { factory { ComponentA() } factory

    { ComponentB(get()) } } module(“C") { factory { ComponentA() } factory { ComponentB(get()) } } } class ComponentB(c : ComponentA) class ComponentC(c : ComponentA)
  53. Namespaces module { module("B") { factory { ComponentA() } factory

    { ComponentB(get()) } } module(“C") { factory { ComponentA() } factory { ComponentB(get()) } } } class ComponentB(c : ComponentA) class ComponentC(c : ComponentA)
  54. Namespaces module { module("B") { factory { ComponentA() } factory

    { ComponentB(get()) } } module(“C") { factory { ComponentA() } factory { ComponentB(get()) } } } class ComponentB(c : ComponentA) class ComponentC(c : ComponentA) // B.ComponentA // B.ComponentB // C.ComponentA // C.ComponentC
  55. Scopes

  56. Scopes module { scope(scopeId = “scope_id”) { … } }

  57. Scopes module { scope(scopeId = “scope_id”) { … } }

  58. Scopes module { scope(scopeId = “scope_id”) { … } }

    // create a scope val session = getKoin().createScope("scope_id") // or get scope if already created before val session = getKoin().getScope("scope_id") // will return the same instance of Presenter until Scope is closed val presenter = get<Presenter>() // close the scope session.close() // instance of presenter has been dropped
  59. Scopes module { scope(scopeId = “scope_id”) { … } }

    // create a scope val session = getKoin().createScope("scope_id") // or get scope if already created before val session = getKoin().getScope("scope_id") // will return the same instance of Presenter until Scope is closed val presenter = get<Presenter>() // close the scope session.close() // instance of presenter has been dropped
  60. Scopes module { scope(scopeId = “scope_id”) { … } }

    // create a scope val session = getKoin().createScope("scope_id") // or get scope if already created before val session = getKoin().getScope("scope_id") // will return the same instance of Presenter until Scope is closed val presenter = get<Presenter>() // close the scope session.close() // instance of presenter has been dropped
  61. Scopes module { scope(scopeId = “scope_id”) { … } }

    // create a scope val session = getKoin().createScope("scope_id") // or get scope if already created before val session = getKoin().getScope("scope_id") // will return the same instance of Presenter until Scope is closed val presenter = get<Presenter>() // close the scope session.close() // instance of presenter has been dropped
  62. Scopes module { scope(scopeId = “scope_id”) { … } }

    // create a scope val session = getKoin().createScope("scope_id") // or get scope if already created before val session = getKoin().getScope("scope_id") // will return the same instance of Presenter until Scope is closed val presenter = get<Presenter>() // close the scope session.close() // instance of presenter has been dropped
  63. Binding scope to lifecycle class MyActivity : AppCompatActivity() { //

    inject Presenter instance, tied to current “session" scope val presenter : Presenter by inject() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) // bind current lifecycle to Activity's scope bindScope(getScope("session")) }
  64. Binding scope to lifecycle class MyActivity : AppCompatActivity() { //

    inject Presenter instance, tied to current “session" scope val presenter : Presenter by inject() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) // bind current lifecycle to Activity's scope bindScope(getScope("session")) }
  65. Logging

  66. class Presenter(repository: Repository) class Repository(dateSource: DateSource) interface DateSource class DebugDataSource()

    : DateSource
  67. module { factory { Presenter(get()) } single { Repository(get()) }

    single<DateSource> { DebugDataSource() } } class Presenter(repository: Repository) class Repository(dateSource: DateSource) interface DateSource class DebugDataSource() : DateSource
  68. module { factory { Presenter(get()) } single { Repository(get()) }

    single<DateSource> { DebugDataSource() } } get<Presenter>() get<Presenter>() class Presenter(repository: Repository) class Repository(dateSource: DateSource) interface DateSource class DebugDataSource() : DateSource
  69. module { factory { Presenter(get()) } single { Repository(get()) }

    single<DateSource> { DebugDataSource() } } get<Presenter>() get<Presenter>() (KOIN)::Resolve [Presenter] ~ Factory[class=Presenter] (KOIN):: Resolve [Repository] ~ Single[class=Repository] (KOIN):: Resolve [Datasource] ~ Single[class=DebugDatasource, binds~(Datasource)] (KOIN):: (*) Created (KOIN):: (*) Created (KOIN)::(*) Created (KOIN)::Resolve [Repository] ~ Factory[class=Repository] (KOIN):: Resolve [DebugDatasource] ~ Single[class=DebugDatasource, binds~(Datasource)] (KOIN)::(*) Created class Presenter(repository: Repository) class Repository(dateSource: DateSource) interface DateSource class DebugDataSource() : DateSource
  70. Missing dependency module { single { ComponentA(get()) } single {

    ComponentB(get()) } } class ComponentA(component: ComponentB) class ComponentB(component: ComponentA) get<ComponentC>()
  71. Missing dependency module { single { ComponentA(get()) } single {

    ComponentB(get()) } } class ComponentA(component: ComponentB) class ComponentB(component: ComponentA) org.koin.error.DependencyResolutionException: No definition found for ComponentC - Check your definitions and contexts visibility get<ComponentC>()
  72. Cyclic dependencies class ComponentA(component: ComponentB) class ComponentB(component: ComponentA)

  73. Cyclic dependencies module { single { ComponentA(get()) } single {

    ComponentB(get()) } } class ComponentA(component: ComponentB) class ComponentB(component: ComponentA)
  74. Cyclic dependencies get<ComponentA>() module { single { ComponentA(get()) } single

    { ComponentB(get()) } } class ComponentA(component: ComponentB) class ComponentB(component: ComponentA)
  75. Cyclic dependencies org.koin.error.BeanInstanceCreationException: Can't create bean Bean[class=ComponentA] due to error

    : BeanInstanceCreationException: Can't create bean Bean[class=ComponentB] due to error : BeanInstanceCreationException: Can't create bean Bean[class=ComponentA] due to error : DependencyResolutionException: Cyclic dependency detected while resolving class ComponentA module { single { ComponentA(get()) } single { ComponentB(get()) } } class ComponentA(component: ComponentB) class ComponentB(component: ComponentA) get<ComponentA>()
  76. Tests

  77. Tests class SampleTest : KoinTest { val presenter : MyPresenter

    by inject() val repository : Repository by inject() @Before fun before(){ startKoin(listOf(myModule)) } @Test fun testSayHello() { val hello = repository.giveHello() assertEquals(hello, presenter.sayHello()) } @After fun after(){ closeKoin() } }
  78. Tests class SampleTest : KoinTest { val presenter : MyPresenter

    by inject() val repository : Repository by inject() @Before fun before(){ startKoin(listOf(myModule)) } @Test fun testSayHello() { val hello = repository.giveHello() assertEquals(hello, presenter.sayHello()) } @After fun after(){ closeKoin() } }
  79. Tests class SampleTest : KoinTest { val presenter : MyPresenter

    by inject() val repository : Repository by inject() @Before fun before(){ startKoin(listOf(myModule)) } @Test fun testSayHello() { val hello = repository.giveHello() assertEquals(hello, presenter.sayHello()) } @After fun after(){ closeKoin() } }
  80. Tests class SampleTest : KoinTest { val presenter : MyPresenter

    by inject() val repository : Repository by inject() @Before fun before(){ startKoin(listOf(myModule)) } @Test fun testSayHello() { val hello = repository.giveHello() assertEquals(hello, presenter.sayHello()) } @After fun after(){ closeKoin() } }
  81. Tests class SampleTest : KoinTest { val presenter : MyPresenter

    by inject() val repository : Repository by inject() @Before fun before(){ startKoin(listOf(myModule)) } @Test fun testSayHello() { val hello = repository.giveHello() assertEquals(hello, presenter.sayHello()) } @After fun after(){ closeKoin() } }
  82. Declare on the fly class SampleTest : KoinTest { @Test

    fun testSayHello() { startKoin(listOf(myModule)) declare { single<Repository> { RepositoryImpl() } } val presenter : MyPresenter = get() assertEquals(hello, presenter.sayHello()) } }
  83. Mock out of the box class SampleTest : KoinTest {

    @Test fun testSayHello() { startKoin(listOf(myModule)) declareMock<Repository>() val presenter : MyPresenter = get() assertEquals(hello, presenter.sayHello()) } }
  84. Graph validation

  85. Graph validation class ComponentA(component: ComponentB) class ComponentB(component: ComponentC) class ComponentC()

  86. Graph validation class ComponentA(component: ComponentB) class ComponentB(component: ComponentC) class ComponentC()

    val sampleModule = module { single { ComponentA(get()) } factory { ComponentB(get()) } }
  87. Graph validation class ComponentA(component: ComponentB) class ComponentB(component: ComponentC) class ComponentC()

    val sampleModule = module { single { ComponentA(get()) } factory { ComponentB(get()) } } class DryRunTest : KoinTest { @Test fun dryRunTest() { startKoin(listOf(sampleModule)) dryRun() } }
  88. Android Arch Components

  89. Default Way class ListFragment : Fragment() { val listViewModel =

    ViewModelProviders.of(this).get(ListViewModel::class.java) }
  90. Simpler with Kotlin class ListFragment : Fragment() { val listViewModel

    = getViewModel<ListViewModel>() } inline fun <reified VM : ViewModel> Fragment.getViewModel():VM { return viewModelProvider.get(VM::class.java) }
  91. ViewModel with arguments class DetailViewModel(id: String) : ViewModel()

  92. ViewModel with arguments class DetailViewModel(id: String) : ViewModel() class DetailFactory(val

    id: String) : ViewModelProvider.Factory { override fun <T : ViewModel> create(modelClass: Class<T>): T { if (modelClass == DetailViewModel::class.java) { return DetailViewModel(id) as T } error("Can't create ViewModel for class='$modelClass'") } }
  93. ViewModel with arguments class DetailViewModel(id: String) : ViewModel() class DetailFactory(val

    id: String) : ViewModelProvider.Factory { override fun <T : ViewModel> create(modelClass: Class<T>): T { if (modelClass == DetailViewModel::class.java) { return DetailViewModel(id) as T } error("Can't create ViewModel for class='$modelClass'") } } class DetailFragment : Fragment() { val listViewModel = getViewModel<DetailViewModel>(DetailFactory(id)) }
  94. Koin Way model { viewModel { (id : String) ->

    DetailViewModel(id) } }
  95. Koin Way model { viewModel { (id : String) ->

    DetailViewModel(id) } }
  96. Koin Way model { viewModel { (id : String) ->

    DetailViewModel(id) } } class DetailFragment : Fragment() { val detailViewModel by viewModel<DetailViewModel> { parametersOf(“sampleId") } }
  97. Koin vs Dagger 2

  98. What missed from Dagger 2?

  99. What missed from Dagger 2? • Compile time validation

  100. What missed from Dagger 2? • Compile time validation •

    Features • Multibindings • Async Injection • Reusable scope
  101. Why use Koin?

  102. Why use Koin? • Dagger 2 is too complex for

    start with DI
  103. Why use Koin? • Dagger 2 is too complex for

    start with DI • No need Dagger 2 advanced features
  104. Why use Koin? • Dagger 2 is too complex for

    start with DI • No need Dagger 2 advanced features • Single place of dependencies declaration
  105. Why use Koin? • Dagger 2 is too complex for

    start with DI • No need Dagger 2 advanced features • Single place of dependencies declaration • DSL is amazing for dependencies declaration
  106. Why use Koin? • Dagger 2 is too complex for

    start with DI • No need Dagger 2 advanced features • Single place of dependencies declaration • DSL is amazing for dependencies declaration • Support Kotlin Multiplatform projects
  107. Why use Koin? • Dagger 2 is too complex for

    start with DI • No need Dagger 2 advanced features • Single place of dependencies declaration • DSL is amazing for dependencies declaration • Support Kotlin Multiplatform projects • Faster build time
  108. Why use Koin? • Dagger 2 is too complex for

    start with DI • No need Dagger 2 advanced features • Single place of dependencies declaration • DSL is amazing for dependencies declaration • Support Kotlin Multiplatform projects • Faster build time • Pure Kotlin code
  109. Thanks insert-Koin.io krl.rozov@gmail.com krlrozov