Android Summit 2019: DIY Dependency Injection with Kotlin

Android Summit 2019: DIY Dependency Injection with Kotlin

5701f31a8433a22ae736282de8d08cd6?s=128

Sam Edwards

August 14, 2019
Tweet

Transcript

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

  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
  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
  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
  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
  6. @HandstandSam #AndroidSummit Why 100% Kotlin?

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

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

    concise.
  9. @HandstandSam #AndroidSummit Kotlin Language Features makes DIY Do-able ! Named

    Parameters ! Default Parameters ! “by lazy” ! () -> lambdas
  10. @HandstandSam #AndroidSummit TL;DR - The DIY Recipe ! Kotlin ✅

    ! Constructor Injection ✅ ! No Annotations or Code Generation ✅ ! Some Hacks for Activities & Fragments
  11. None
  12. @HandstandSam #AndroidSummit Why Dependency Injection? ! It enables all the

    really cool things I like to talk about
  13. @HandstandSam #AndroidSummit It Enables Testing DevFest Florida

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

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

  16. @HandstandSam #AndroidSummit Dependency Injection is Not Magical

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

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

    “Swapping”
  19. @HandstandSam #AndroidSummit Dependency Injection is Passing in dependencies
 instead of

    pre-defining them. Inversion of Control
  20. @HandstandSam #AndroidSummit Dependency Injection is Inversion of Control Code Dependency

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

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

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

    SystemOutLogger or Logger
  24. @HandstandSam #AndroidSummit Dependency Injection is Inversion of Control Code AndroidLogger

    SystemOutLogger or Logger
  25. @HandstandSam #AndroidSummit Dependency Injection is Inversion of Control Code AndroidLogger

    SystemOutLogger or Logger “Inversion of Control" aka “Swapping”
  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
  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)
  28. @HandstandSam #AndroidSummit Common Types of Dependency Injection

  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()
  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() )
  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
  32. @HandstandSam #AndroidSummit Why DIY Constructor Injection Makes Sense ! Compile

    Time Safety ! No 3rd party library ! No magic ! Easy to “Find Usages” and understand
  33. @HandstandSam #AndroidSummit Aren’t Their Libraries That Do DI for You?

    ! Yes, let’s take a look.
  34. @HandstandSam #AndroidSummit Open Source DI Libraries ! Dagger ! Toothpick

    ! Koin ! Kodein
  35. @HandstandSam #AndroidSummit ! Dagger ! Toothpick ! Koin ! Kodein

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

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

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

    Code Generation YES YES NO NO Single
 Instance NO NO YES NO ❌ ❌ ❌
  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 ❌ ❌ ❌
  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 ❌ ❌ ❌ ❌
  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 ❌ ❌ ❌ ❌
  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
  43. @HandstandSam #AndroidSummit DIY Dependency Injection in Practice

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

  45. @HandstandSam #AndroidSummit Network Graph interface NetworkGraph { val categoryRepo: CategoryRepo

    val itemRepo: ItemRepo val userRepo: UserRepo }
  46. @HandstandSam #AndroidSummit Create Graphs of Dependencies • Similar to Dagger

    @Component OkHttpClient Services Retrofit NetworkGraph Url Repos
  47. @HandstandSam #AndroidSummit Session Graph interface SessionGraph { val sessionManager: SessionManager

    val shoppingCart: ShoppingCart val userPreferences: UserPreferences }
  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
  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
  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) }
  51. @HandstandSam #AndroidSummit ! Create as needed ! Create Graph during

    Application::onCreate() if required by Activity/ Fragment for possible onProcessRestore bugs Setting up Your Graphs
  52. @HandstandSam #AndroidSummit GRAPH BEING SETUP

  53. @HandstandSam #AndroidSummit GRAPH BEING ACCESSED IN ACTIVITY

  54. @HandstandSam #AndroidSummit RECAP: The DIY Recipe

  55. @HandstandSam #AndroidSummit The DIY Recipe ! Kotlin ✅ ! Constructor

    Injection ✅ ! No Annotations or Code Generation ✅ ! Some Hacks for Activities & Fragments
  56. @HandstandSam #AndroidSummit Challenges - Nothing is Perfect ! Static Availability

    for Activities and Fragments ! Onboarding ! Scoping
  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
  58. @HandstandSam #AndroidSummit Thank you.