Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Mobile Fest 2018. Кирилл Розов. Insert Koin

26 views

Published on

Koin - следующий шаг на пути становления проектов полностью на Kotlin. Что внутри Koin? Только DSL и магия Kotlin. Давайте выясним правда ли Dependency Injection + Kotlin + Easy of Use == Koin?

Published in: Education
  • Be the first to comment

  • Be the first to like this

Mobile Fest 2018. Кирилл Розов. Insert Koin

  1. 1. Kiev 2018 Absolute Mobile Fest Kirill Rozov Android Developer
  2. 2. Kiev 2018Insert Koin Dependency Injection
  3. 3. Kiev 2018Insert Koin 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. 4. Kiev 2018Insert Koin Inversion of Control CLIENT CLIENT CLIENT DEPENDENCY 1 DEPENDENCY 2 DEPENDENCY 3 DEPENDENCY 4
  5. 5. Kiev 2018Insert Koin Inversion of Control DEPENDENCY 1 DEPENDENCY 2 DEPENDENCY 3 DEPENDENCY 4 IoC CONTAINER CLIENT CLIENT CLIENT
  6. 6. Kiev 2018Insert Koin Inversion of Control DEPENDENCY 1 DEPENDENCY 2 DEPENDENCY 3 DEPENDENCY 4 IoC CONTAINER CLIENT CLIENT CLIENT
  7. 7. Kiev 2018Insert Koin Popular DI • Guice • Spring DI • Square Dagger • Google Dagger 2 • Java EE CDI • PicoContainer • Kodein • Koin
  8. 8. Kiev 2018Insert Koin insert-Koin.io
  9. 9. Kiev 2018Insert Koin
  10. 10. Kiev 2018Insert Koin Sample val sampleModule = applicationContext { bean { ComponentA() } }
  11. 11. Kiev 2018Insert Koin Sample val sampleModule = applicationContext { bean { ComponentA() } }
  12. 12. Kiev 2018Insert Koin Sample val sampleModule = applicationContext { bean { ComponentA() } } class Application : KoinComponent { val component by inject<ComponentA>() }
  13. 13. Kiev 2018Insert Koin Sample val sampleModule = applicationContext { bean { ComponentA() } } class Application : KoinComponent { val component by inject<ComponentA>() }
  14. 14. Kiev 2018Insert Koin Sample val sampleModule = applicationContext { bean { ComponentA() } } class Application : KoinComponent { val component by inject<ComponentA>() } fun main(vararg args: String) { startKoin(listOf(sampleModule)) }
  15. 15. Kiev 2018Insert Koin Init class SampleApplication : Application() { override fun onCreate() { startKoin(this, listOf(sampleModule)) } } fun main(vararg args: String) { start(listOf(sampleModule)) { … } } fun main(vararg args: String) { startKoin(listOf(sampleModule)) }
  16. 16. Kiev 2018Insert Koin KoinComponent fun <T> KoinComponent.inject(name: String) : Lazy<T> fun <T> KoinComponent.inject(name: String, paras: Parameters) : Lazy<T> fun <T> KoinComponent.property(key: String) : T fun <T> KoinComponent.property(key: String, defaultValue: T) : T fun <T> KoinComponent.get(name: String) : T fun <T> KoinComponent.get(name: String, params: Parameters) : T fun KoinComponent.setProperty(key: String, value: Any) fun KoinComponent.releaseContext(name: String) fun KoinComponent.releaseProperties(vararg keys: String) interface KoinComponent
  17. 17. Kiev 2018Insert Koin KoinComponent fun <T> KoinComponent.inject(name: String) : Lazy<T> fun <T> KoinComponent.inject(name: String, paras: Parameters) : Lazy<T> fun <T> KoinComponent.property(key: String) : T fun <T> KoinComponent.property(key: String, defaultValue: T) : T fun <T> KoinComponent.get(name: String) : T fun <T> KoinComponent.get(name: String, params: Parameters) : T fun KoinComponent.setProperty(key: String, value: Any) fun KoinComponent.releaseContext(name: String) fun KoinComponent.releaseProperties(vararg keys: String)
  18. 18. Kiev 2018Insert Koin KoinComponent fun <T> ComponentCallbacks.inject(name: String) : Lazy<T> fun <T> ComponentCallbacks.inject(name: String, paras: Parameters) : Lazy<T> fun <T> ComponentCallbacks.property(key: String) : T fun <T> ComponentCallbacks.property(key: String, defaultValue: T) : T fun <T> ComponentCallbacks.get(name: String) : T fun <T> ComponentCallbacks.get(name: String, params: Parameters) : T fun ComponentCallbacks.setProperty(key: String, value: Any) fun ComponentCallbacks.releaseContext(name: String) fun ComponentCallbacks.releaseProperties(vararg keys: String)
  19. 19. Kiev 2018Insert Koin Provide dependencies applicationContext { bean { ComponentA() } factory { ComponentB() } }
  20. 20. Kiev 2018Insert Koin Provide dependencies applicationContext { bean { ComponentA() } factory { ComponentB() } }
  21. 21. Kiev 2018Insert Koin Provide dependencies class ComponentA(c : ComponentB)
  22. 22. Kiev 2018Insert Koin Provide dependencies applicationContext { bean { ComponentB() } bean { ComponentA(get<ComponentB>()) } } class ComponentA(c : ComponentB)
  23. 23. Kiev 2018Insert Koin Named dependencies applicationContext { bean { ComponentB(…) } bean { ComponentB(…) } }
  24. 24. Kiev 2018Insert Koin Named dependencies applicationContext { bean(“debug”) { ComponentB(…) } bean(“prod”) { ComponentB(…) } }
  25. 25. Kiev 2018Insert Koin Named dependencies applicationContext { bean(“debug”) { ComponentB(…) } bean(“prod”) { ComponentB(…) } bean { ComponentA(get(“debug”)) } }
  26. 26. Kiev 2018Insert Koin Named dependencies applicationContext { bean(“debug”) { ComponentB() } bean(“prod”) { ComponentB() } bean { ComponentA(get(“debug”)) } } class Application : KoinComponent { val componentProd by inject<ComponentB>(“prod”) val componentDebug by inject<ComponentB>(“debug”) }
  27. 27. Kiev 2018Insert Koin Type binding applicationContext { bean { ComponentImpl() } bean { ComponentImpl() as Component } bean { ComponentImpl() } bind Component::class }
  28. 28. Kiev 2018Insert Koin Dependency Parameters
  29. 29. Kiev 2018Insert Koin Properties class RestService(url: String)
  30. 30. Kiev 2018Insert Koin Properties applicationContext { bean { RestService(getProperty(“url”)) } } class RestService(url: String)
  31. 31. Kiev 2018Insert Koin Properties applicationContext { bean { RestService(getProperty(“url”)) } } class RestService(url: String) applicationContext { bean { RestService(getProperty(“url”, “http://localhost”)) } }
  32. 32. Kiev 2018Insert Koin Properties applicationContext { bean { RestService(getProperty(“url”)) } } class RestService(url: String) applicationContext { bean { RestService(getProperty(“url”, “http://localhost”)) } } val key1Property: String by property(“key1”)
  33. 33. Kiev 2018Insert Koin Properties startKoin(properties: Map<String, Any> = mapOf(“key” to 1))
  34. 34. Kiev 2018Insert Koin Properties Sources • koin.properties in JAR resources • koin.properties in assets • Environment properties
  35. 35. Kiev 2018Insert Koin Environment variables startKoin(useEnvironmentProperties = true) // Get user name from properties val key1Property: String by property(“USER”)
  36. 36. Kiev 2018Insert Koin Problem class NewsDetailPresenter(newsId : String)
  37. 37. Kiev 2018Insert Koin Parameters val presenter: NewsDetailPresenter by inject { mapOf(NEWS_ID to "sample") }
  38. 38. Kiev 2018Insert Koin Parameters applicationContext { factory { params: ParametersProvider -> NewsDetailsPresenter(params[NEWS_ID]) } } val presenter: NewsDetailPresenter by inject { mapOf(NEWS_ID to "sample") }
  39. 39. Kiev 2018Insert Koin Parameters applicationContext { factory { params: ParametersProvider -> NewsDetailsPresenter(params[NEWS_ID]) } } val presenter: NewsDetailPresenter by inject { mapOf(NEWS_ID to "sample") } interface ParametersProvider { operator fun <T> get(key: String): T fun <T> getOrNull(key: String): T? }
  40. 40. Kiev 2018Insert Koin Contexts
  41. 41. Kiev 2018Insert Koin Contexts applicationContext { context("main") { bean { ComponentA() } bean { ComponentB() } } }
  42. 42. Kiev 2018Insert Koin Contexts applicationContext { context("main") { bean { ComponentA() } bean { ComponentB() } } } class MainActivity : Activity() { override fun onStop() { releaseContext("main") } }
  43. 43. Kiev 2018Insert Koin Context isolation applicationContext { context("A") { bean { ComponentA() } context("B") { bean { ComponentB() } } } context("C") { bean { ComponentС() } } }
  44. 44. Kiev 2018Insert Koin Context isolation applicationContext { context("A") { bean { ComponentA() } context("B") { bean { ComponentB() } } } context("C") { bean { ComponentС() } } }
  45. 45. Kiev 2018Insert Koin Context isolation applicationContext { context("A") { bean { ComponentA() } context("B") { bean { ComponentB() } } } context("C") { bean { ComponentС() } } }
  46. 46. Kiev 2018Insert Koin Context isolation applicationContext { context("A") { bean { ComponentA() } context("B") { bean { ComponentB() } } } context("C") { bean { ComponentС() } } }
  47. 47. Kiev 2018Insert Koin Logging
  48. 48. Kiev 2018Insert Koin class Presenter(repository: Repository) class Repository(dateSource: DateSource) interface DateSource class DebugDataSource() : DateSource
  49. 49. Kiev 2018Insert Koin applicationContext { factory { Presenter(get()) } bean { Repository(get()) } bean { DebugDataSource() } bind DateSource::class } class Presenter(repository: Repository) class Repository(dateSource: DateSource) interface DateSource class DebugDataSource() : DateSource
  50. 50. Kiev 2018Insert Koin applicationContext { factory { Presenter(get()) } bean { Repository(get()) } bean { DebugDataSource() } bind DateSource::class } get<Presenter>() get<Presenter>() class Presenter(repository: Repository) class Repository(dateSource: DateSource) interface DateSource class DebugDataSource() : DateSource
  51. 51. Kiev 2018Insert Koin applicationContext { factory { Presenter(get()) } bean { Repository(get()) } bean { DebugDataSource() } bind DateSource::class } get<Presenter>() get<Presenter>() (KOIN) :: Resolve [Presenter] ~ Factory[class=Presenter] (KOIN) :: Resolve [Repository] ~ Bean[class=Repository] (KOIN) :: Resolve [Datasource] ~ Bean[class=DebugDatasource, binds~(Datasource)] (KOIN) :: (*) Created (KOIN) :: (*) Created (KOIN) :: (*) Created (KOIN) :: Resolve [Repository] ~ Factory[class=Repository] (KOIN) :: Resolve [DebugDatasource] ~ Bean[class=DebugDatasource, binds~(Datasource)] (KOIN) :: (*) Created class Presenter(repository: Repository) class Repository(dateSource: DateSource) interface DateSource class DebugDataSource() : DateSource
  52. 52. Kiev 2018Insert Koin Missing dependency applicationContext { bean { ComponentA(get()) } bean { ComponentB(get()) } } class ComponentA(component: ComponentB) class ComponentB(component: ComponentA) get<ComponentC>()
  53. 53. Kiev 2018Insert Koin Missing dependency applicationContext { bean { ComponentA(get()) } bean { 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>()
  54. 54. Kiev 2018Insert Koin Cyclic dependencies class ComponentA(component: ComponentB) class ComponentB(component: ComponentA)
  55. 55. Kiev 2018Insert Koin Cyclic dependencies applicationContext { bean { ComponentA(get()) } bean { ComponentB(get()) } } class ComponentA(component: ComponentB) class ComponentB(component: ComponentA)
  56. 56. Kiev 2018Insert Koin Cyclic dependencies get<ComponentA>() applicationContext { bean { ComponentA(get()) } bean { ComponentB(get()) } } class ComponentA(component: ComponentB) class ComponentB(component: ComponentA)
  57. 57. Kiev 2018Insert Koin 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 applicationContext { bean { ComponentA(get()) } bean { ComponentB(get()) } } class ComponentA(component: ComponentB) class ComponentB(component: ComponentA) get<ComponentA>()
  58. 58. Kiev 2018Insert Koin Tests
  59. 59. Kiev 2018Insert Koin 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() } }
  60. 60. Kiev 2018Insert Koin 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() } }
  61. 61. Kiev 2018Insert Koin 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() } }
  62. 62. Kiev 2018Insert Koin 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() } }
  63. 63. Kiev 2018Insert Koin Graph validation
  64. 64. Kiev 2018Insert Koin Graph validation class ComponentA(component: ComponentB) class ComponentB(component: ComponentC) class ComponentC()
  65. 65. Kiev 2018Insert Koin Graph validation class ComponentA(component: ComponentB) class ComponentB(component: ComponentC) class ComponentC() val sampleModule = applicationContext { bean { ComponentA(get()) } factory { ComponentB(get()) } }
  66. 66. Kiev 2018Insert Koin Graph validation class ComponentA(component: ComponentB) class ComponentB(component: ComponentC) class ComponentC() val sampleModule = applicationContext { bean { ComponentA(get()) } factory { ComponentB(get()) } } class DryRunTest : KoinTest { @Test fun dryRunTest() { startKoin(listOf(sampleModule)) dryRun() } }
  67. 67. Kiev 2018Insert Koin Android Arch Components
  68. 68. Kiev 2018Insert Koin Default Way class ListFragment : Fragment() { val listViewModel = ViewModelProviders.of(this).get(ListViewModel::class.java) }
  69. 69. Kiev 2018Insert Koin Simpler with Kotlin class ListFragment : Fragment() { val listViewModel = getViewModel<ListViewModel>() } inline fun <reified VM : ViewModel> Fragment.getViewModel():VM { return viewModelProvider.get(VM::class.java) }
  70. 70. Kiev 2018Insert Koin ViewModel with arguments class DetailViewModel(id: String) : ViewModel()
  71. 71. Kiev 2018Insert Koin 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'") } }
  72. 72. Kiev 2018Insert Koin 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 ListFragment : Fragment() { val listViewModel = provideViewModel<DetailViewModel>(DetailFactory(id)) }
  73. 73. Kiev 2018Insert Koin Koin Way applicationContext { viewModel { params -> DetailViewModel(params["id"]) } }
  74. 74. Kiev 2018Insert Koin Koin Way applicationContext { viewModel { params -> DetailViewModel(params["id"]) } } class ListFragment : Fragment() { val listViewModel by viewModel<ListViewModel> { mapOf("id" to "sample") } }
  75. 75. Kiev 2018Insert Koin Koin vs Dagger 2
  76. 76. Kiev 2018Insert Koin What missed from Dagger 2? • Compile time validation • Features • Multibindings • Async Injection • Reusable scope
  77. 77. Kiev 2018Insert Koin 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
  78. 78. Kiev 2018Insert Koin Thanks insert-Koin.io kiryl.rozau@apalon.com krlrozov

×