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

Dependency injection from zero to hero with Koin

Dependency injection from zero to hero with Koin

Koin (https://insert-koin.io) is a pragmatic dependency injection framework for Kotlin/Android developers. Let's see how it will make your life easier thanks to the power of Kotlin language: from basics to the setup of Android architecture's components, without forgetting the testing part... And of course, how to migrate your “old” Dagger project.

At the menu also, the presentation of the new 2.0 release and some advanced APIs!

(light/online version)

Arnaud GIULIANI

June 07, 2019
Tweet

More Decks by Arnaud GIULIANI

Other Decks in Technology

Transcript

  1. class ElectricHeater : Heater { var heating: Boolean = false

    override fun on() { println("~ ~ ~ heating ~ ~ ~") heating = true } override fun off() { heating = false } override fun isHot(): Boolean = heating }
  2. class Thermosiphon(val heater: Heater) : Pump{ override fun pump() {

    if (heater.isHot()){ println("=> => pumping => =>") } } }
  3. class CoffeeMaker(val pump: Pump, val heater: Heater) { fun brew()

    { heater.on() pump.pump() println(" [_]P coffee! [_]P ") heater.off() } }
  4. class CoffeeApp { val coffeeMaker: CoffeeMaker fun run() { coffeeMaker.brew()

    } } fun main(vararg args: String) { startKoin(listOf(coffeeMakerModule)) CoffeeApp().run() }
  5. repositories { jcenter() } dependencies { // Koin for Kotlin

    implementation ‘org.koin:koin-core:$version’ } Gradle Setup
  6. class ElectricHeater : Heater class Thermosiphon(val heater: Heater) : Pump

    class CoffeeMaker(val pump: Pump, val heater: Heater) val coffeeMakerModule = module { single { CoffeeMaker(get(),get()) } single<Pump>{ Thermosiphon(get()) } single<Heater> { ElectricHeater() } } * also multi modules
  7. class ElectricHeater : Heater class Thermosiphon(val heater: Heater) : Pump

    class CoffeeMaker(val pump: Pump, val heater: Heater) val coffeeMaker = module { single { CoffeeMaker(get(),get()) } } val coffeeStuffs = module { single<Pump>{ Thermosiphon(get()) } single<Heater> { ElectricHeater() } } * also multi modules
  8. ✅ open access to Kotlin extensions: inject(), get() identify a

    class linked to the Koin API KoinComponent is an interface marker bootstrap & help integrate with runtime (Android…)
  9. class CoffeeApp : KoinComponent { val coffeeMaker: CoffeeMaker by inject()

    fun run() { coffeeMaker.brew() } } fun main(vararg args: String) { startKoin(listOf(coffeeMakerModule)) CoffeeApp().run() }
  10. fun run() { coffeeMaker.brew() } } fun main(vararg args: String)

    { startKoin { modules(coffeeMakerModule) } CoffeeApp().run() }
  11. ~ ~ ~ heating ~ ~ ~ => => pumping

    => => [_]P coffee! [_]P
  12. val coffeeMakerModule = module { single { CoffeeMaker(get(),lazy { get<Heater()

    }) } single<Pump>{ Thermosiphon(get()) } single<Heater> { ElectricHeater() } }
  13. class CoffeeMaker(private val pump: Pump, _heater: Lazy<Heater>) { val heater

    by _heater fun brew() { heater.on() pump.pump() println(" [_]P coffee! [_]P ") heater.off() } } * Lazy is a Kotlin type
  14. class Thermosiphon @Inject constructor(private val heater: Heater) : Pump{ override

    fun pump() { if (heater.isHot()){ println("=> => pumping => =>") } } }
  15. class CoffeeMaker @Inject constructor(private val pump: Pump, private val _heater:

    Lazy<Heater>) { val heater by lazy { _heater.get() } fun brew() { heater.on() pump.pump() println(" [_]P coffee! [_]P ") heater.off() } } * Lazy is a Dagger type
  16. class Thermosiphon @Inject constructor(private val heater: Heater) : Pump{ override

    fun pump() { if (heater.isHot()){ println("=> => pumping => =>") } } }
  17. class Thermosiphon : Pump, KoinComponent { private val heater: Heater

    by inject() override fun pump() { if (heater.isHot()){ println("=> => pumping => =>") } } }
  18. class CoffeeMaker @Inject constructor(private val pump: Pump, private val _heater:

    Lazy<Heater>) { val heater: Heater by lazy { _heater.get() } fun brew() { heater.on() pump.pump() println(" [_]P coffee! [_]P ") heater.off() } }
  19. class CoffeeMaker @Inject constructor(private val pump: Pump) : KoinComponent val

    heater: Heater by inject() fun brew() { heater.on() pump.pump() println(" [_]P coffee! [_]P ") heater.off() } }
  20. repositories { jcenter() } dependencies { // Koin for Android

    implementation ‘org.koin:koin-android:$version’ } Gradle Setup
  21. class MyApplication : Application() { override fun onCreate() { super.onCreate()

    // Reference Android context in Koin startKoin { androidLogger() androidContext(this@MyApplication) modules(coffeeMakerModule) } } }
  22. class MyComponent(val context : Context) val myModule = module {

    single { MyComponent(androidContext()) } }
  23. class MyPresenter val myModule = module { // will create

    MyPresenter on each call factory { MyPresenter() } } class MyActivity : AppCompatActivity() { }
  24. class MyPresenter val myModule = module { // will create

    MyPresenter on each call factory { MyPresenter() } } class MyActivity : AppCompatActivity() { // new instance on each call val presenter: MyPresenter by inject() }
  25. repositories { jcenter() } dependencies { // Koin for Android

    + ViewModel features implementation ‘org.koin:koin-android-viewmodel:$version’ } Gradle Setup
  26. class MyViewModel(val coffeeMaker : CoffeeMaker) : ViewModel() val coffeeMakerModule =

    module { viewModel { MyViewModel(get()) } } class MyActivity : AppCompatActivity() { // inject ViewModel val myViewModel: MyViewModel by viewModel() override fun onCreate(savedInstanceState: Bundle?) { // get ViewModel val myViewModel: MyViewModel = getViewModel() }
  27. //... viewModel { MyViewModel(get()) } } class MyActivity : AppCompatActivity()

    { // inject ViewModel val myViewModel: MyViewModel by viewModel() override fun onCreate(savedInstanceState: Bundle?) { // get ViewModel val myViewModel: MyViewModel = getViewModel() }
  28. //... viewModel { MyViewModel(get()) } } class MyFragment : Fragment()

    { // inject ViewModel val myViewModel: MyViewModel by viewModel() override fun onCreate(savedInstanceState: Bundle?) { // get ViewModel val myViewModel: MyViewModel = getViewModel() }
  29. //... viewModel { MyViewModel(get()) } } class MyFragment : Fragment()

    { // inject ViewModel from parent activity val myViewModel: MyViewModel by sharedViewModel() overrid e fun onCreate(savedInstanceState: Bundle?) { // get ViewModel val myViewModel: MyViewModel = getViewModel() }
  30. //... viewModel { MyViewModel(get()) } } class MyFragment : Fragment()

    { // inject ViewModel from parent activity val myViewModel: MyViewModel by sharedViewModel() overrid e fun onCreate(savedInstanceState: Bundle?) { // get ViewModel val myViewModel: MyViewModel = getViewModel() } val viewModel = ViewModelProviders .of(this.activity) .get(MyViewModel::class.java)
  31. repositories { jcenter() } dependencies { // Koin for Tests

    testImplementation ‘org.koin:koin-test:$version’ } Gradle Setup
  32. class ModuleTest { @Test fun `check all definitions from coffeeMakerModule`()

    { startKoin { modules(coffeeMakerModule) }.checkModules() } }
  33. class CoffeeMakerTest : KoinTest { val coffeeMaker: CoffeeMaker by inject()

    val heater: Heater by inject() @Test fun testHeaterIsTurnedOnAndThenOff() { startKoin { modules(coffeeMakerModule) } declareMock<Heater>() given(heater.isHot()).will { true } coffeeMaker.brew() Mockito.verify(heater, times(1)).on() Mockito.verify(heater, times(1)).off() } }
  34. class CoffeeMakerTest : KoinTest { val coffeeMaker: CoffeeMaker by inject()

    val heater: Heater by inject() @Test fun testHeaterIsTurnedOnAndThenOff() { startKoin { modules(coffeeMakerModule) } declareMock<Heater>() given(heater.isHot()).will { true } coffeeMaker.brew() Mockito.verify(heater, times(1)).on() Mockito.verify(heater, times(1)).off() } }
  35. class CoffeeMakerTest : KoinTest { val coffeeMaker: CoffeeMaker by inject()

    val heater: Heater by inject() @Test fun testHeaterIsTurnedOnAndThenOff() { startKoin { modules(coffeeMakerModule) } declareMock<Heater>() given(heater.isHot()).will { true } coffeeMaker.brew() verify(heater, times(1)).on() verify(heater, times(1)).off() } }
  36. Some API Break - Simplified module DSL (no more inner

    modules) - Revamped Scope API - Context isolation & startKoin DSL (koinApplication) - Definition resolution (default & qualifiers)
  37. - Container DSL - Qualifiers (string/type) - Global vs isolated

    context - Scope API - Android, Scopes & lifecycle - Injection Parameters - AndroidX - Koin for Java - Koin for Ktor
  38. What is a scope? A scope is a context with

    fixed duration of time, in which an object exists. When the scope ends, any objects bound under that scope cannot be injected again.
  39. module { scope(named("Session")) { scoped { SessionData() } } }

    // create scope with id "session1" and qualifier "Session" val session1 = getKoin().createScope("session1",named("Session")) // get SessionData instance from scope "session1" val data : SessionData = session1.get() // close instances scope "session1" scope.close()
  40. module { scope(named("Session")) { scoped { SessionData() } } }

    // create scope with id "session1" and qualifier "Session" val session1 = getKoin().createScope("session1",named("Session")) // get SessionData instance from scope "session1" val data : SessionData = session1.get() // close instances scope "session1" session1.close()
  41. repositories { jcenter() } dependencies { // Koin for Android

    + Scope feature implementation ‘org.koin:koin-android-scope:$version’ } Gradle Setup
  42. module { scope(named<MyActivity>()) { scoped { MyPresenter() } } }

    class MyActivity : AppCompatActivity { val presenter : MyPresenter by currentScope.inject() } ** follow lifecycle * currentScope is provided by extension
  43. version = “2.0.1" repositories { jcenter() } dependencies { //

    Kotlin Core features implementation "org.koin:koin-core:$version" // Android basic features implementation "org.koin:koin-android:$version" // Android basic + scope features implementation "org.koin:koin-android-scope:$version" // Android basic + scope + viewmodel features
  44. dependencies { // Kotlin Core features implementation "org.koin:koin-core:$version" // Android

    basic features implementation "org.koin:koin-android:$version" // Android basic + scope features implementation "org.koin:koin-android-scope:$version" // Android basic + scope + viewmodel features implementation "org.koin:koin-android-viewmodel:$version" // Kotlin Checks + JUnit & Mock features testImplementation "org.koin:koin-test:$version" }
  45. dependencies { // Kotlin Core features implementation "org.koin:koin-core:$version" // Android

    basic features implementation "org.koin:koin-android:$version" // Android basic + scope features implementation "org.koin:koin-android-scope:$version" // Android basic + scope + viewmodel features implementation "org.koin:koin-android-viewmodel:$version" // Kotlin Checks + JUnit & Mock features testImplementation "org.koin:koin-test:$version" }
  46. dependencies { // Kotlin Core features implementation "org.koin:koin-core:$version" // Android

    basic features implementation "org.koin:koin-android:$version" // Android basic + scope features implementation "org.koin:koin-android-scope:$version" // Android basic + scope + viewmodel features implementation "org.koin:koin-android-viewmodel:$version" // Kotlin Checks + JUnit & Mock features testImplementation "org.koin:koin-test:$version" }
  47. dependencies { // Kotlin Core features implementation "org.koin:koin-core:$version" // Android

    basic features implementation "org.koin:koin-android:$version" // Android basic + scope features implementation "org.koin:koin-android-scope:$version" // Android basic + scope + viewmodel features implementation "org.koin:koin-android-viewmodel:$version" // Kotlin Checks + JUnit & Mock features testImplementation "org.koin:koin-test:$version" }
  48. dependencies { // Kotlin Core features implementation "org.koin:koin-core:$version" // Android

    basic features implementation "org.koin:koin-android:$version" // Android basic + scope features implementation "org.koin:koin-android-scope:$version" // Android basic + scope + viewmodel features implementation "org.koin:koin-android-viewmodel:$version" // Kotlin Checks + JUnit & Mock features testImplementation "org.koin:koin-test:$version" }