$30 off During Our Annual Pro Sale. View Details »

Android Summit 2019: DIY Dependency Injection with Kotlin

Android Summit 2019: DIY Dependency Injection with Kotlin

Sam Edwards

August 14, 2019
Tweet

More Decks by Sam Edwards

Other Decks in Programming

Transcript

  1. @HandstandSam #AndroidSummit
    DIY Dependency
    Injection with Kotlin
    Sam Edwards -

    View Slide

  2. @HandstandSam #AndroidSummit
    Why am I up here talking about this?
    ! Demystify the topic of Dependency Injection
    ! Show how you can implement DIY, if you choose

    View Slide

  3. @HandstandSam #AndroidSummit
    Our Team’s Story
    ! Got tasked to build a reusable SDK
    ! This was the first time we were building a complex SDK

    View Slide

  4. @HandstandSam #AndroidSummit
    Product Requirements
    ! SDK will be used by different apps, in different codebases
    ! Should not assume clients will be using a 3rd party library
    ! Stay lean and lightweight
    ! Make the SDK configurable

    View Slide

  5. @HandstandSam #AndroidSummit
    Technical Requirements
    ! Reactive Programming
    ! Immutable Data
    ! Modular Code
    ! Single Responsibility Code
    ! Well Tested Code
    ! Avoid 3rd Party Libraries
    ! 100% Kotlin
    ! Dependency Injection

    View Slide

  6. @HandstandSam #AndroidSummit
    Why 100% Kotlin?

    View Slide

  7. @HandstandSam #AndroidSummit
    Why 100% Kotlin?
    makes coding easy and concise.

    View Slide

  8. @HandstandSam #AndroidSummit
    Why 100% Kotlin?
    makes dependency injection easy and concise.

    View Slide

  9. @HandstandSam #AndroidSummit
    Kotlin Language Features makes DIY Do-able
    ! Named Parameters
    ! Default Parameters
    ! “by lazy”
    ! () -> lambdas

    View Slide

  10. @HandstandSam #AndroidSummit
    TL;DR - The DIY Recipe
    ! Kotlin ✅
    ! Constructor Injection ✅
    ! No Annotations or Code Generation ✅
    ! Some Hacks for Activities & Fragments

    View Slide

  11. View Slide

  12. @HandstandSam #AndroidSummit
    Why Dependency Injection?
    ! It enables all the really cool things I like to talk about

    View Slide

  13. @HandstandSam #AndroidSummit
    It Enables Testing
    DevFest Florida

    View Slide

  14. @HandstandSam #AndroidSummit
    It Enables Custom Debug Features
    Droidcon Boston

    View Slide

  15. @HandstandSam #AndroidSummit
    It Enable Mock Flavors
    Droidcon NYC

    View Slide

  16. @HandstandSam #AndroidSummit
    Dependency Injection is Not Magical

    View Slide

  17. @HandstandSam #AndroidSummit
    Dependency Injection is Not Magical
    DI !=

    View Slide

  18. @HandstandSam #AndroidSummit
    Dependency Injection is
    Use THIS instead of THAT.
    “Swapping”

    View Slide

  19. @HandstandSam #AndroidSummit
    Dependency Injection is
    Passing in dependencies

    instead of pre-defining them.
    Inversion of Control

    View Slide

  20. @HandstandSam #AndroidSummit
    Dependency Injection is Inversion of Control
    Code
    Dependency

    View Slide

  21. @HandstandSam #AndroidSummit
    Dependency Injection is Inversion of Control
    Code

    Dependency
    Logger

    View Slide

  22. @HandstandSam #AndroidSummit
    Dependency Injection is Inversion of Control
    Code

    AndroidLogger
    Logger

    View Slide

  23. @HandstandSam #AndroidSummit
    Dependency Injection is Inversion of Control
    Code

    AndroidLogger SystemOutLogger
    or
    Logger

    View Slide

  24. @HandstandSam #AndroidSummit
    Dependency Injection is Inversion of Control
    Code

    AndroidLogger SystemOutLogger
    or
    Logger

    View Slide

  25. @HandstandSam #AndroidSummit
    Dependency Injection is Inversion of Control
    Code

    AndroidLogger SystemOutLogger
    or
    Logger
    “Inversion of Control" aka “Swapping”

    View Slide

  26. @HandstandSam #AndroidSummit
    Avoid Static Instances - “new” is Your Friend
    Log.d("TAG", “message”)
    ! Global state
    ! Must be mutable to be “reset”
    between tests
    ! Hard to mock in tests -
    PowerMock

    View Slide

  27. @HandstandSam #AndroidSummit
    No Dependency Injection
    class NoInjectionRepository {
    val log = AndroidLogger()
    val database = SqlDelightDatabase()
    fun updateUser(user: User) {
    log.log("Updating User: $user")
    database.save(user)
    }
    }
    NoInjectionRepository().updateUser(user)

    View Slide

  28. @HandstandSam #AndroidSummit
    Common Types
    of Dependency Injection

    View Slide

  29. @HandstandSam #AndroidSummit
    Setter Injection
    class SetterInjectionRepository {
    lateinit var log: Logger
    lateinit var database: Database
    fun updateUser(user: User) {
    log.log("Updating User: $user")
    database.save(user)
    }
    }
    val setterInjection = SetterInjectionRepository()
    setterInjection.log = AndroidLogger()
    setterInjection.database = SqlDelightDatabase()

    View Slide

  30. @HandstandSam #AndroidSummit
    Constructor Injection
    class ConstructorInjectionRepository(
    val logger: Logger,
    val database: Database
    ) {
    fun updateUser(user: User) {
    log.log("Updating User: $user")
    database.save(user)
    }
    }
    ConstructorInjectionRepository(
    logger = AndroidLogger(),
    database = SqlDelightDatabase()
    )

    View Slide

  31. @HandstandSam #AndroidSummit
    class ConstructorInjectionRepository(
    val log: Logger = AndroidLogger(),
    val database: Database
    ) {
    fun updateUser(user: User) {
    log.log("Updating User: $user")
    database.save(user)
    }
    }
    ConstructorInjectionRepository(
    database = SqlDelightDatabase()
    )
    Why Constructor Injection is Great for Kotlin
    Default Parameters
    Named Parameters

    View Slide

  32. @HandstandSam #AndroidSummit
    Why DIY Constructor Injection Makes Sense
    ! Compile Time Safety
    ! No 3rd party library
    ! No magic
    ! Easy to “Find Usages” and understand

    View Slide

  33. @HandstandSam #AndroidSummit
    Aren’t Their Libraries That Do DI for You?
    ! Yes, let’s take a look.

    View Slide

  34. @HandstandSam #AndroidSummit
    Open Source DI Libraries
    ! Dagger
    ! Toothpick
    ! Koin
    ! Kodein

    View Slide

  35. @HandstandSam #AndroidSummit
    ! Dagger
    ! Toothpick
    ! Koin
    ! Kodein
    Code
    Generation
    YES
    YES
    NO
    NO

    View Slide

  36. @HandstandSam #AndroidSummit
    ! Dagger
    ! Toothpick
    ! Koin
    ! Kodein
    Code
    Generation
    YES
    YES
    NO
    NO


    View Slide

  37. @HandstandSam #AndroidSummit
    ! Dagger
    ! Toothpick
    ! Koin
    ! Kodein
    Code
    Generation
    YES
    YES
    NO
    NO
    Single

    Instance
    NO
    NO
    YES
    NO


    View Slide

  38. @HandstandSam #AndroidSummit
    ! Dagger
    ! Toothpick
    ! Koin
    ! Kodein
    Code
    Generation
    YES
    YES
    NO
    NO
    Single

    Instance
    NO
    NO
    YES
    NO



    View Slide

  39. @HandstandSam #AndroidSummit
    ! Dagger
    ! Toothpick
    ! Koin
    ! Kodein
    Code
    Generation
    YES
    YES
    NO
    NO
    Single

    Instance
    NO
    NO
    YES
    NO
    Available

    at the time
    YES
    YES
    YES
    NO



    View Slide

  40. @HandstandSam #AndroidSummit
    ! Dagger
    ! Toothpick
    ! Koin
    ! Kodein
    Code
    Generation
    YES
    YES
    NO
    NO
    Single

    Instance
    NO
    NO
    YES
    NO
    Available

    at the time
    YES
    YES
    YES
    NO




    View Slide

  41. @HandstandSam #AndroidSummit
    ! Dagger
    ! Toothpick
    ! Koin
    ! Kodein
    Code
    Generation
    YES
    YES
    NO
    NO
    Single

    Instance
    NO
    NO
    YES
    NO
    Available

    at the time
    YES
    YES
    YES
    NO
    Adds dependency
    on another library
    YES
    YES
    YES
    YES




    View Slide

  42. @HandstandSam #AndroidSummit
    So, How Much Work is DIY?
    ! Not that much
    ! Slightly more verbose
    ! Have to manually build advanced DI features
    ! Great for most use cases when you don’t already know a DI library
    really well

    View Slide

  43. @HandstandSam #AndroidSummit
    DIY Dependency Injection
    in Practice

    View Slide

  44. @HandstandSam #AndroidSummit
    ShoppingApp - Open Source on GitHub
    https:/
    /github.com/handstandsam/ShoppingApp

    View Slide

  45. @HandstandSam #AndroidSummit
    Network Graph
    interface NetworkGraph {
    val categoryRepo: CategoryRepo
    val itemRepo: ItemRepo
    val userRepo: UserRepo
    }

    View Slide

  46. @HandstandSam #AndroidSummit
    Create Graphs of Dependencies
    • Similar to Dagger @Component
    OkHttpClient Services
    Retrofit
    NetworkGraph
    Url
    Repos

    View Slide

  47. @HandstandSam #AndroidSummit
    Session Graph
    interface SessionGraph {
    val sessionManager: SessionManager
    val shoppingCart: ShoppingCart
    val userPreferences: UserPreferences
    }

    View Slide

  48. @HandstandSam #AndroidSummit
    Multiple or Nested Graphs are
    AppGraph
    SessionGraph
    NetworkGraph
    ! Each module project needs their own graph
    ! Each graph is similar to a Dagger @Component

    View Slide

  49. @HandstandSam #AndroidSummit
    ! Each Module can contain it’s own graph
    ! Doesn’t need a static instance unless there are Activities or
    Fragments
    Graphs in Modules

    View Slide

  50. @HandstandSam #AndroidSummit
    ! Use the `by lazy` language feature of Kotlin to delay initialization, but
    cache result.
    Leveraging `by lazy`
    class AppGraph {
    val networkGraph by lazy NetworkGraph(config = config)
    }

    View Slide

  51. @HandstandSam #AndroidSummit
    ! Create as needed
    ! Create Graph during Application::onCreate() if required by Activity/
    Fragment for possible onProcessRestore bugs
    Setting up Your Graphs

    View Slide

  52. @HandstandSam #AndroidSummit
    GRAPH BEING SETUP

    View Slide

  53. @HandstandSam #AndroidSummit
    GRAPH BEING ACCESSED
    IN ACTIVITY

    View Slide

  54. @HandstandSam #AndroidSummit
    RECAP: The DIY Recipe

    View Slide

  55. @HandstandSam #AndroidSummit
    The DIY Recipe
    ! Kotlin ✅
    ! Constructor Injection ✅
    ! No Annotations or Code Generation ✅
    ! Some Hacks for Activities & Fragments

    View Slide

  56. @HandstandSam #AndroidSummit
    Challenges - Nothing is Perfect
    ! Static Availability for Activities and Fragments
    ! Onboarding
    ! Scoping

    View Slide

  57. @HandstandSam #AndroidSummit
    Key Takeaways
    ! You don’t HAVE to use a Dependency Injection Library, Dagger
    ! There are no magic annotations. @Inject (JSR-330)
    ! Kotlin language features make this concise and straightforward
    ! Dependency Injection is critical for any app

    View Slide

  58. @HandstandSam #AndroidSummit
    Thank you.

    View Slide