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

Fundamentals of Dependency Injection - Droidcon...

Fundamentals of Dependency Injection - Droidcon NYC

Dependency Injection (DI) plays a key role in building scalable software projects. The Android community has spent the last decade experimenting and iterating on DI approaches primarily focused on the benefits of facilitating testing and building more modular apps. If you weren't in the industry during that experimentation and iteration it can be easy to miss how we got to where we are today.

In this talk, we're going back to basics! We're going to look at Dependency Injection, to help you understand the concepts behind the pattern, irrespective of which library, if any, you use to implement it. We'll look at the basic approaches to dependency substitution, see what problems DI can help solve, and learn how to make our own DI implementation.

Avatar for Ryan Harter

Ryan Harter

June 30, 2025
Tweet

More Decks by Ryan Harter

Other Decks in Technology

Transcript

  1. class MyPresenter { Analytics() Repository() OtherRepository() private val analytics: Analytics

    = = = private val otherRepository: OtherRepository private val repository: Repository }
  2. class MyPresenter { Analytics() Repository() OtherRepository() private val analytics: Analytics

    = = = private val otherRepository: OtherRepository private val repository: Repository }
  3. class MyPresenter { Analytics() Repository() OtherRepository() private val analytics: Analytics

    = = = private val otherRepository: OtherRepository private val repository: Repository }
  4. class MyPresenter private val analytics: private val otherRepository: private val

    repository: ) ( , , , Analytics Repository OtherRepository
  5. class MyPresenter private val analytics: private val otherRepository: private val

    repository: ) ( , , , Analytics Repository OtherRepository
  6. class MyPresenter private val analytics: private val otherRepository: private val

    repository: ) ( , , , Analytics Repository OtherRepository Analytics Repository OtherRepository
  7. class Analytics class Repository class OtherRepository class MyPresenter private val

    analytics: private val otherRepository: private val repository: ) ( , , , Analytics Repository OtherRepository
  8. class class class class MyPresenter private val analytics: private val

    otherRepository: private val repository: ) ( , , , Analytics Repository OtherRepository Analytics Repository OtherRepository
  9. class class class private val analytics: private val otherRepository: private

    val repository: ) ( , , , Analytics Repository OtherRepository Analytics Repository OtherRepository MyPresenter class
  10. private val analytics: private val otherRepository: private val repository: )

    ( Analytics Repository OtherRepository , , , MyPresenter class
  11. private val analytics: private val otherRepository: private val repository: )

    ( Analytics Repository OtherRepository Analytics() Repository() OtherRepository() = = = , , , MyPresenter class
  12. MyPresenter fun getMyPresenter(): class ObjectGraph { } private fun getAnalytics():

    Analytics private fun getRepository(): Repository private fun getOtherRepository(): OtherRepository ...
  13. MyPresenter fun getMyPresenter(): class ObjectGraph { } = MyPresenter( )

    getAnalytics(), getRepository(), getOtherRepository(), private fun getAnalytics(): Analytics private fun getRepository(): Repository private fun getOtherRepository(): OtherRepository
  14. MyPresenter fun getMyPresenter(): } = MyPresenter( ) getAnalytics(), getRepository(), getOtherRepository(),

    class ObjectGraph { private fun getAnalytics(): Analytics private fun getRepository(): Repository private fun getOtherRepository(): OtherRepository
  15. MyPresenter fun getMyPresenter(): class ObjectGraph } = MyPresenter( ) getAnalytics(),

    getRepository(), getOtherRepository(), { private val application: Application, ) ( private fun getAnalytics(): Analytics private fun getRepository(): Repository private fun getOtherRepository(): OtherRepository
  16. MyPresenter fun getMyPresenter(): ObjectGraph } = MyPresenter( ) getAnalytics(), getRepository(),

    getOtherRepository(), { private val application: Application, ) ( private fun getAnalytics(): Analytics private fun getRepository(): Repository private fun getOtherRepository(): OtherRepository class
  17. MyPresenter fun getMyPresenter(): } = MyPresenter( ) getAnalytics(), getRepository(), getOtherRepository(),

    { private val application: Application, ) ( private fun getAnalytics(): Analytics private fun getRepository(): Repository private fun getOtherRepository(): OtherRepository Component class
  18. class private val analytics: private val otherRepository: private val repository:

    ( Analytics Repository OtherRepository , , , MyPresenter )
  19. private val analytics: private val otherRepository: private val repository: )

    Analytics Repository OtherRepository , , , class class MyPresenterFactory ( MyPresenter
  20. private val analytics: private val otherRepository: private val repository: )

    Analytics Repository OtherRepository , , , class class MyPresenterFactory ( MyPresenter
  21. private val analytics: private val otherRepository: private val repository: )

    Analytics Repository OtherRepository , , , class class MyPresenterFactory ( MyPresenter fun get(): MyPresenter } {
  22. private val analytics: private val otherRepository: private val repository: )

    Analytics Repository OtherRepository , , , class class MyPresenterFactory ( MyPresenter fun get(): MyPresenter } {
  23. private val analytics: private val otherRepository: private val repository: )

    Analytics Repository OtherRepository , , , class class MyPresenterFactory ) fun get(): MyPresenter } ( MyPresenter { ( private val analyticsFactory: private val repositoryFactory: private val otherRepositoryFactory: , , , Analytics Repository OtherRepository Factory Factory Factory
  24. private val analytics: private val otherRepository: private val repository: )

    Analytics Repository OtherRepository , , , class class MyPresenterFactory ) fun get(): MyPresenter = MyPresenter( ) } analyticsFactory.get(), repositoryFactory.get(), otherRepositoryFactory.get(), ( MyPresenter { ( private val analyticsFactory: private val repositoryFactory: private val otherRepositoryFactory: , , , Analytics Repository OtherRepository Factory Factory Factory
  25. private val analytics: private val otherRepository: private val repository: )

    Analytics Repository OtherRepository , , , class class MyPresenterFactory ) fun get(): MyPresenter = MyPresenter( ) analyticsFactory.get(), repositoryFactory.get(), otherRepositoryFactory.get(), ( MyPresenter { ( interface Provider<T> { fun get(): T } private val analyticsFactory: private val repositoryFactory: private val otherRepositoryFactory: , , , Analytics Repository OtherRepository Factory Factory Factory
  26. private val otherRepository: ) OtherRepository, class MyPresenterFactory fun get(): MyPresenter

    = MyPresenter( ) } analyticsFactory.get(), repositoryFactory.get(), otherRepositoryFactory.get(), ( interface Provider<T> { fun get(): T } ) { Provider<T> : override private val analyticsFactory: private val repositoryFactory: private val otherRepositoryFactory: , , , Analytics Repository OtherRepository Factory Factory Factory
  27. private val otherRepository: ) OtherRepository, class MyPresenterFactory private val analyticsFactory:

    private val repositoryFactory: private val otherRepositoryFactory: fun get(): MyPresenter = MyPresenter( ) } analyticsFactory.get(), repositoryFactory.get(), otherRepositoryFactory.get(), ( Analytics , interface Provider<T> { fun get(): T } ) { Provider<T> : override Provider< > Repository , Provider< > OtherRepository , Provider< >
  28. private val analytics: private val otherRepository: private val repository: )

    Analytics Repository OtherRepository , , , class class MyPresenterFactory private val analyticsFactory: private val repositoryFactory: RepositoryFactory, private val otherRepositoryFactory: OtherRepositoryFactory, ) fun get(): MyPresenter = MyPresenter( ) } analyticsFactory.get(), repositoryFactory.get(), otherRepositoryFactory.get(), ( MyPresenter { ( AnalyticsFactory,
  29. private val analytics: private val otherRepository: private val repository: )

    Analytics Repository OtherRepository , , , class class MyPresenterFactory private val analyticsFactory: private val repositoryFactory: RepositoryFactory, private val otherRepositoryFactory: OtherRepositoryFactory, ) fun get(): MyPresenter = MyPresenter( ) } analyticsFactory.get(), repositoryFactory.get(), otherRepositoryFactory.get(), ( MyPresenter { ( AnalyticsFactory,
  30. class fun get(): Analytics = Analytics() } { AnalyticsFactory class

    Analytics { ... } val sessionId: String = randomString()
  31. class fun get(): Analytics = Analytics() } { AnalyticsFactory class

    Analytics { ... } val sessionId: String = randomString()
  32. class fun get(): Analytics = Analytics() } { AnalyticsFactory class

    Analytics { val sessionId: String ... } = randomString()
  33. class fun get(): Analytics = Analytics() } { AnalyticsFactory class

    Analytics { ... } val sessionId: String = randomString()
  34. class fun get(): Analytics = Analytics() } { AnalyticsFactory class

    Analytics { ... } val sessionId: String = randomString() val f = AnalyticsFactory()
  35. class fun get(): Analytics = Analytics() } { AnalyticsFactory class

    Analytics { ... } val sessionId: String = randomString() val f = AnalyticsFactory() f.get().sessionId
  36. class fun get(): Analytics = Analytics() } { AnalyticsFactory class

    Analytics { ... } val sessionId: String = randomString() val f = AnalyticsFactory() f.get().sessionId = 49bf5857-d468-4efd-a3cd-9e16044b6aaa
  37. class fun get(): Analytics = Analytics() } { AnalyticsFactory class

    Analytics { ... } val sessionId: String = randomString() val f = AnalyticsFactory() f.get().sessionId f.get().sessionId = 49bf5857-d468-4efd-a3cd-9e16044b6aaa
  38. class fun get(): Analytics = Analytics() } { AnalyticsFactory class

    Analytics { ... } val sessionId: String = randomString() val f = AnalyticsFactory() f.get().sessionId f.get().sessionId = 1f49aee6-2bf4-499e-bc5f-7a83d03453b4 = 49bf5857-d468-4efd-a3cd-9e16044b6aaa
  39. class fun get(): Analytics = Analytics() } { AnalyticsFactory class

    Analytics { ... } val sessionId: String = randomString()
  40. class fun get(): Analytics } { AnalyticsFactory class Analytics {

    ... } val sessionId: String = randomString() { }
  41. class fun get(): Analytics } { AnalyticsFactory class Analytics {

    ... } val sessionId: String = randomString() { } private var _instance
  42. class fun get(): Analytics } { AnalyticsFactory class Analytics {

    ... } val sessionId: String = randomString() { } private var _instance: Any
  43. class fun get(): Analytics } { AnalyticsFactory class Analytics {

    ... } val sessionId: String = randomString() { } private var _instance: Any private val none = Any()
  44. class fun get(): Analytics } { AnalyticsFactory class Analytics {

    ... } val sessionId: String = randomString() { } private var _instance: Any private val none = Any() = none
  45. class fun get(): Analytics } { AnalyticsFactory class Analytics {

    ... } val sessionId: String = randomString() { } private var _instance: Any private val none = Any() = none if (_instance == none) { _instance = Analytics() }
  46. class fun get(): Analytics } { AnalyticsFactory class Analytics {

    ... } val sessionId: String = randomString() { } private var _instance: Any private val none = Any() = none if (_instance == none) { _instance = Analytics() } return _instance as Analytics val f = AnalyticsFactory()
  47. class fun get(): Analytics } { AnalyticsFactory { } private

    var _instance: Any private val none = Any() = none if (_instance == none) { _instance = Analytics() } return _instance as Analytics val f = AnalyticsFactory() f.get().sessionId f.get().sessionId = 49bf5857-d468-4efd-a3cd-9e16044b6aaa = 49bf5857-d468-4efd-a3cd-9e16044b6aaa
  48. class fun get(): Analytics { AnalyticsFactory class Analytics { ...

    } val sessionId: String = randomString() { private var _instance: Any private val none = Any() = none if (_instance == none) { _instance = Analytics()
  49. class fun get(): Analytics { AnalyticsFactory class Analytics { ...

    } val sessionId: String = randomString() { private var _instance: Any private val none = Any() = none if (_instance == none) { _instance = Analytics()
  50. String String Tag1 + Tag2 + Analytics + Repository +

    Other Repository + ∅ ∅ ∅ API + ∅ DB + ∅ MyPresenter + ∅
  51. data class DependencyId( val qualifier: Qualifier?, val klass: KClass<*>, )

    class Analytics { ... } @Inject constructor( ) @SessionId , val sessionId: String
  52. data class DependencyId( val qualifier: Qualifier?, val klass: KClass<*>, )

    val dependencyGraph: Map<DependencyId, Provider<*>> class Analytics { ... } @Inject constructor( ) @SessionId , val sessionId: String
  53. data class DependencyId( val qualifier: Qualifier?, val klass: KClass<*>, )

    val dependencyGraph: Map<DependencyId, Provider<*>> mapOf( = DependencyId(SessionId::class, String::class) to SessionIdFactory, ) class Analytics { ... } @Inject constructor( ) @SessionId , val sessionId: String
  54. class Analytics { ... } @Inject constructor( ) @SessionId ,

    val sessionId: String mapOf( DependencyId(SessionId::class, String::class) to SessionIdFactory, ) DependencyId(null, Analytics::class) to AnalyticsFactory, val dependencyGraph: Map<DependencyId, Provider<*>> = data class DependencyId( val qualifier: Qualifier?, val klass: KClass<*>, )
  55. class ObjectGraph } { private val application: Application, ) (

    private fun getAnalytics(): Analytics private fun getNetworkClient(): NetworkClient
  56. class ObjectGraph } { private val application: Application, ) (

    class UserApi @Inject constructor( @UserId userId: String, client: NetworkClient, analytics: Analytics, ) { // ... } private fun getAnalytics(): Analytics private fun getNetworkClient(): NetworkClient
  57. class ObjectGraph } { private val application: Application, ) (

    class UserApi @Inject constructor( @UserId userId: String, client: NetworkClient, analytics: Analytics, ) { // ... } private fun getAnalytics(): Analytics private fun getNetworkClient(): NetworkClient
  58. class ObjectGraph } { private val application: Application, ) (

    class UserApi @Inject constructor( @UserId userId: String, client: NetworkClient, analytics: Analytics, ) { // ... } private fun getAnalytics(): Analytics private fun getNetworkClient(): NetworkClient
  59. class ObjectGraph } { private val application: Application, ) (

    class UserApi @Inject constructor( @UserId userId: String, client: NetworkClient, analytics: Analytics, ) { // ... } private fun getAnalytics(): Analytics private fun getNetworkClient(): NetworkClient
  60. class ObjectGraph } { private val application: Application, ) (

    class UserApi @Inject constructor( @UserId userId: String, client: NetworkClient, analytics: Analytics, ) { // ... } private fun getAnalytics(): Analytics private fun getNetworkClient(): NetworkClient fun getUserApi(@UserId userId: String): UserApi = UserApi(userId, getNetworkClient(), getAnalytics())
  61. class ObjectGraph } { private val application: Application, ) (

    class UserApi @Inject constructor( @UserId userId: String, client: NetworkClient, analytics: Analytics, ) { // ... } private fun getAnalytics(): Analytics private fun getNetworkClient(): NetworkClient
  62. class ObjectGraph } { private val application: Application, ) (

    class UserApi @Inject constructor( @UserId userId: String, client: NetworkClient, analytics: Analytics, ) { // ... } private fun getAnalytics(): Analytics private fun getNetworkClient(): NetworkClient
  63. class ObjectGraph } { private val application: Application, ) (

    class UserApi @Inject constructor( @UserId userId: String, client: NetworkClient, analytics: Analytics, ) { // ... } private fun getAnalytics(): Analytics private fun getNetworkClient(): NetworkClient inner class ChildGraph
  64. class ObjectGraph } { private val application: Application, ) (

    class UserApi @Inject constructor( @UserId userId: String, client: NetworkClient, analytics: Analytics, ) { // ... } private fun getAnalytics(): Analytics private fun getNetworkClient(): NetworkClient inner class ChildGraph( @UserId private val userId: String )
  65. class ObjectGraph } { private val application: Application, ) (

    class UserApi @Inject constructor( @UserId userId: String, client: NetworkClient, analytics: Analytics, ) { private fun getAnalytics(): Analytics private fun getNetworkClient(): NetworkClient inner class ChildGraph( @UserId private val userId: String ) { } fun getUserApi(): UserApi = UserApi( ) getAnalytics() getNetworkClient(), , userId,
  66. class ObjectGraph } { private val application: Application, ) (

    class UserApi @Inject constructor( @UserId userId: String, client: NetworkClient, analytics: Analytics, ) { private fun getAnalytics(): Analytics private fun getNetworkClient(): NetworkClient inner class ChildGraph( @UserId private val userId: String ) { } fun getUserApi(): UserApi = UserApi( ) getAnalytics() getNetworkClient(), , userId,
  67. class ObjectGraph } { private val application: Application, ) (

    class UserApi @Inject constructor( @UserId userId: String, client: NetworkClient, analytics: Analytics, ) { private fun getAnalytics(): Analytics private fun getNetworkClient(): NetworkClient inner class ChildGraph( @UserId private val userId: String ) { } fun getUserApi(): UserApi = UserApi( ) getAnalytics() getNetworkClient(), , userId,
  68. class ObjectGraph } { private val application: Application, ) (

    class UserApi @Inject constructor( @UserId userId: String, private fun getAnalytics(): Analytics private fun getNetworkClient(): NetworkClient inner class ChildGraph( @UserId private val userId: String ) { } fun getUserApi(): UserApi = UserApi( ) getAnalytics() getNetworkClient(), , userId, @UserId userId: String fun createChildGraph( ): ChildGraph = ChildGraph(userId)
  69. client: NetworkClient, analytics: Analytics, ) { // ... } val

    childGraph = objectGraph.createChildGraph(userId) childGraph.getUserApi()
  70. client: NetworkClient, analytics: Analytics, ) { // ... } val

    childGraph = objectGraph.createChildGraph(userId) childGraph.getUserApi()
  71. Recap • Dependency injection is complicated • Key concepts •

    Object Graph • Factories • Object Lifetime • Quali fi ers • Child Graphs