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

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 Why am I up here talking about this?

    ! Demystify the topic of Dependency Injection ! Show how you can implement DIY, if you choose
  2. @HandstandSam #AndroidSummit Our Team’s Story ! Got tasked to build

    a reusable SDK ! This was the first time we were building a complex SDK
  3. @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
  4. @HandstandSam #AndroidSummit Technical Requirements ! Reactive Programming ! Immutable Data

    ! Modular Code ! Single Responsibility Code ! Well Tested Code ! Avoid 3rd Party Libraries ! 100% Kotlin ! Dependency Injection
  5. @HandstandSam #AndroidSummit Kotlin Language Features makes DIY Do-able ! Named

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

    ! Constructor Injection ✅ ! No Annotations or Code Generation ✅ ! Some Hacks for Activities & Fragments
  7. @HandstandSam #AndroidSummit Dependency Injection is Inversion of Control Code AndroidLogger

    SystemOutLogger or Logger “Inversion of Control" aka “Swapping”
  8. @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
  9. @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)
  10. @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()
  11. @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() )
  12. @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
  13. @HandstandSam #AndroidSummit Why DIY Constructor Injection Makes Sense ! Compile

    Time Safety ! No 3rd party library ! No magic ! Easy to “Find Usages” and understand
  14. @HandstandSam #AndroidSummit ! Dagger ! Toothpick ! Koin ! Kodein

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

    Code Generation YES YES NO NO Single
 Instance NO NO YES NO ❌ ❌ ❌
  16. @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 ❌ ❌ ❌
  17. @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 ❌ ❌ ❌ ❌
  18. @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 ❌ ❌ ❌ ❌
  19. @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
  20. @HandstandSam #AndroidSummit Create Graphs of Dependencies • Similar to Dagger

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

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

    Application::onCreate() if required by Activity/ Fragment for possible onProcessRestore bugs Setting up Your Graphs
  26. @HandstandSam #AndroidSummit The DIY Recipe ! Kotlin ✅ ! Constructor

    Injection ✅ ! No Annotations or Code Generation ✅ ! Some Hacks for Activities & Fragments
  27. @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