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

Dagger Party Tricks

Zac Sweers
February 19, 2019

Dagger Party Tricks

Neat things you can do with dagger and common libraries/patterns in modern android development.

Given at Londroid on 2019/02/19 and Droidcon NYC on 2019/08/26

Zac Sweers

February 19, 2019
Tweet

More Decks by Zac Sweers

Other Decks in Programming

Transcript

  1. @Provides fun provideClient(): OkHttpClient { return OkHttpClient.Builder() .build() }b @Provides

    fun provideRetrofit(client: OkHttpClient): Retrofit { return Retrofit.Builder() .client(client) .build() }a
  2. @Provides fun provideCache(ctx: Context): Cache { return Cache(ctx.cacheDir, CACHE_SIZE) }c

    @Provides fun provideClient(cache: Cache): OkHttpClient { return OkHttpClient.Builder() .cache(cache) .build() }b @Provides fun provideRetrofit(client: OkHttpClient): Retrofit { return Retrofit.Builder() .client(client) .build() }a
  3. @Provides fun provideCache(ctx: Context): Cache { return Cache(ctx.cacheDir, CACHE_SIZE) }c

    @Provides fun provideClient(cache: Cache): OkHttpClient { return OkHttpClient.Builder() .cache(cache) .build() }b @Provides fun provideRetrofit(client: OkHttpClient): Retrofit { return Retrofit.Builder() .client(client) .build() }a
  4. Courtesy of Nick Butcher (@crafty) @Provides fun provideCache(ctx: Context): Cache

    { return Cache(ctx.cacheDir, CACHE_SIZE) }c @Provides fun provideClient(cache: Cache): OkHttpClient { return OkHttpClient.Builder() .cache(cache) .build() }b @Provides fun provideRetrofit(client: OkHttpClient): Retrofit { return Retrofit.Builder() .client(client) .build() }a @Inject constructor( retrofit: Retrofit )e
  5. Courtesy of Nick Butcher (@crafty) @Provides fun provideCache(ctx: Context): Cache

    { return Cache(ctx.cacheDir, CACHE_SIZE) }c @Provides fun provideClient(cache: Cache): OkHttpClient { return OkHttpClient.Builder() .cache(cache) .build() }b @Provides fun provideRetrofit(client: OkHttpClient): Retrofit { return Retrofit.Builder() .client(client) .build() }a @Inject constructor( retrofit: Retrofit )e
  6. Courtesy of Nick Butcher (@crafty) @Provides fun provideCache(ctx: Context): Cache

    { return Cache(ctx.cacheDir, CACHE_SIZE) }c @Provides fun provideClient(cache: Cache): OkHttpClient { return OkHttpClient.Builder() .cache(cache) .build() }b @Provides fun provideRetrofit(client: OkHttpClient): Retrofit { return Retrofit.Builder() .client(client) .build() }a @Inject constructor( retrofit: Retrofit )e
  7. Courtesy of Nick Butcher (@crafty) @Provides fun provideCache(ctx: Context): Cache

    { return Cache(ctx.cacheDir, CACHE_SIZE) }c @Provides fun provideClient(cache: Cache): OkHttpClient { return OkHttpClient.Builder() .cache(cache) .build() }b @Provides fun provideRetrofit(client: OkHttpClient): Retrofit { return Retrofit.Builder() .client(client) .build() }a @Inject constructor( retrofit: Retrofit )e
  8. Courtesy of Nick Butcher (@crafty) @Provides fun provideCache(ctx: Context): Cache

    { return Cache(ctx.cacheDir, CACHE_SIZE) }c @Provides fun provideClient(cache: Cache): OkHttpClient { return OkHttpClient.Builder() .cache(cache) .build() }b @Provides fun provideRetrofit(client: OkHttpClient): Retrofit { return Retrofit.Builder() .client(client) .build() }a 100-150ms! @Inject constructor( retrofit: Retrofit )e
  9. Courtesy of Nick Butcher (@crafty) @Provides fun provideCache(ctx: Context): Cache

    { return Cache(ctx.cacheDir, CACHE_SIZE) }c @Provides fun provideClient(cache: Cache): OkHttpClient { return OkHttpClient.Builder() .cache(cache) .build() }b @Provides fun provideRetrofit(client: OkHttpClient): Retrofit { return Retrofit.Builder() .client(client) .build() }a 100-150ms! @Inject constructor( retrofit: Retrofit )e
  10. Courtesy of Nick Butcher (@crafty) @Provides fun provideCache(ctx: Context): Cache

    { return Cache(ctx.cacheDir, CACHE_SIZE) }c @Provides fun provideClient(cache: Cache): OkHttpClient { return OkHttpClient.Builder() .cache(cache) .build() }b @Provides fun provideRetrofit(client: OkHttpClient): Retrofit { return Retrofit.Builder() .client(client) .build() }a @Inject constructor( retrofit: Lazy<Retrofit> )e 100-150ms!
  11. Courtesy of Nick Butcher (@crafty) @Provides fun provideCache(ctx: Context): Cache

    { return Cache(ctx.cacheDir, CACHE_SIZE) }c @Provides fun provideClient(cache: Cache): OkHttpClient { return OkHttpClient.Builder() .cache(cache) .build() }b @Provides fun provideRetrofit(client: OkHttpClient): Retrofit { return Retrofit.Builder() .client(client) .build() }a @Inject constructor( retrofit: Lazy<Retrofit> )e 100-150ms! retrofit.get().create(...)
  12. Courtesy of Nick Butcher (@crafty) @Provides fun provideCache(ctx: Context): Cache

    { return Cache(ctx.cacheDir, CACHE_SIZE) }c @Provides fun provideClient(cache: Cache): OkHttpClient { return OkHttpClient.Builder() .cache(cache) .build() }b @Provides fun provideRetrofit(client: OkHttpClient): Retrofit { return Retrofit.Builder() .client(client) .build() }a @Inject constructor( retrofit: Lazy<Retrofit> )e 100-150ms! retrofit.get().create(...)
  13. Courtesy of Nick Butcher (@crafty) @Provides fun provideCache(ctx: Context): Cache

    { return Cache(ctx.cacheDir, CACHE_SIZE) }c @Provides fun provideClient(cache: Cache): OkHttpClient { return OkHttpClient.Builder() .cache(cache) .build() }b @Provides fun provideRetrofit(client: OkHttpClient): Retrofit { return Retrofit.Builder() .client(client) .build() }a @Inject constructor( retrofit: Lazy<Retrofit> )e 100-150ms! retrofit.get().create(...)
  14. Courtesy of Nick Butcher (@crafty) @Provides fun provideCache(ctx: Context): Cache

    { return Cache(ctx.cacheDir, CACHE_SIZE) }c @Provides fun provideClient(cache: Cache): OkHttpClient { return OkHttpClient.Builder() .cache(cache) .build() }b @Provides fun provideRetrofit(client: OkHttpClient): Retrofit { return Retrofit.Builder() .client(client) .build() }a @Inject constructor( retrofit: Lazy<Retrofit> )e 100-150ms! retrofit.get().create(...) Main thread!
  15. Courtesy of Nick Butcher (@crafty) @Provides fun provideCache(ctx: Context): Cache

    { return Cache(ctx.cacheDir, CACHE_SIZE) }c @Provides fun provideClient(cache: Cache): OkHttpClient { return OkHttpClient.Builder() .cache(cache) .build() }b @Provides fun provideRetrofit( client: Lazy<OkHttpClient>): Retrofit { return Retrofit.Builder() .callFactory { client.get().newCall(it) } .build() }a @Inject constructor( retrofit: Retrofit )e 100-150ms! Background thread
  16. Courtesy of Nick Butcher (@crafty) @Provides fun provideCache(ctx: Context): Cache

    { checkMainThread() return Cache(ctx.cacheDir, CACHE_SIZE) }c @Provides fun provideClient(cache: Cache): OkHttpClient { checkMainThread() return OkHttpClient.Builder() .cache(cache) .build() }b @Provides fun provideRetrofit( client: Lazy<OkHttpClient>): Retrofit { return Retrofit.Builder() .callFactory { client.get().newCall(it) } .build() }a @Inject constructor( retrofit: Retrofit )e 100-150ms! Background thread
  17. @Module class MyGiantModule { @Provides fun provideSeasoning() = Seasoning() @Provides

    fun provideTaco(seasoning: Seasoning) = Taco(seasoning) }a
  18. @Module class MyGiantModuleBase { @Provides fun provideSeasoning() = Seasoning() }a

    @Module(includes = MyGiantModuleBase::class) class MyGiantModule { @Provides fun provideTaco(seasoning: Seasoning) = Taco(seasoning) }b
  19. @Module(includes = MyGiantModuleBase::class) class MyGiantModule { @Provides fun provideTaco(seasoning: Seasoning)

    = Taco(seasoning) }b @Module class MyGiantModuleBase { @Provides fun provideSeasoning() = Seasoning() }a
  20. @Module class MyGiantModule { @Provides fun provideSeasoning() = Seasoning() @Provides

    fun provideTaco(seasoning: Seasoning) = Taco(seasoning) }a
  21. @Module class SeasoningModule { @Provides fun provideSeasoning() = Seasoning() }b

    @Module(includes = SeasoningModule::class) class MyGiantModule { @Provides fun provideTaco(seasoning: Seasoning) = Taco(seasoning) }a
  22. @Module class SeasoningModule { @Provides fun provideSeasoning() = Seasoning() }b

    @Module(includes = [SeasoningModule::class, TacoModule::class]) class MyGiantModule @Module class TacoModule { @Provides fun provideTaco(seasoning: Seasoning) = Taco(seasoning) }c
  23. @Module class SeasoningModule { @Provides fun provideSeasoning() = Seasoning() }b

    @Module(includes = TacoModule::class) class MyGiantModule @Module(includes = SeasoningModule::class) class TacoModule { @Provides fun provideTaco(seasoning: Seasoning) = Taco(seasoning) }c
  24. @Module class SeasoningModule { @Provides fun provideSeasoning() = Seasoning() }b

    @Module(includes = SeasoningModule::class) class TacoModule { @Provides fun provideTaco(seasoning: Seasoning) = Taco(seasoning) }c
  25. @Module class FoodModule { @Provides fun provideSeasoning() = Seasoning() @Provides

    fun provideTaco(seasoning: Seasoning) = Taco(seasoning) }
  26. @Qualifier private annotation class InternalApi @Module class FoodModule { @Provides

    fun provideSeasoning() = Seasoning() @Provides fun provideTaco(seasoning: Seasoning) = Taco(seasoning) }a
  27. @Qualifier private annotation class InternalApi @Module class FoodModule { @InternalApi

    @Provides fun provideSeasoning() = Seasoning() @Provides fun provideTaco(@InternalApi seasoning: Seasoning) = Taco(seasoning) }a
  28. @Module class FoodModule { @Provides fun provideSeasoning() = Seasoning() @Provides

    fun provideTaco(seasoning: Seasoning) = Taco(seasoning) }a
  29. @Module class FoodModule { @Provides fun provideSpicy() = SpicySeasoning() @Provides

    fun provideElPaso() = ElPasoSeasoning() @Provides fun provideTaco( spicy: SpicySeasoning, elpaso: ElPasoSeasoning ) = Taco(setOf(spicy, elpaso)) }a
  30. @Module abstract class FoodModule { @Multibinds abstract fun seasonings(): Set<Seasoning>

    @Provides fun provideSpicy() = SpicySeasoning() @Provides fun provideElPaso() = ElPasoSeasoning() @Provides fun provideTaco( spicy: SpicySeasoning, elpaso: ElPasoSeasoning ) = Taco(setOf(spicy, elpaso)) }a
  31. @Module abstract class FoodModule { @Multibinds abstract fun seasonings(): Set<Seasoning>

    @Provides @IntoSet fun provideSpicy() = SpicySeasoning() @Provides @IntoSet fun provideElPaso() = ElPasoSeasoning() @Provides fun provideTaco(seasonings: Set<Seasoning>) = Taco(seasonings) }a
  32. @Module(includes = SeasoningModule::class) abstract class FoodModule { @Multibinds abstract fun

    seasonings(): Set<Seasoning> @Provides fun provideTaco(seasonings: Set<Seasoning>) = Taco(seasonings) }a @Module class SeasoningModule { @Provides @IntoSet fun provideSpicy() = SpicySeasoning() @Provides @IntoSet fun provideElPaso() = ElPasoSeasoning() }
  33. @Module abstract class FoodModule { @Multibinds abstract fun seasonings(): Set<Seasoning>

    @Provides fun provideTaco(seasonings: Set<Seasoning>) = Taco(seasonings) }a
  34. @Module abstract class NetworkModule { @Multibinds abstract fun interceptors(): Set<Interceptor>

    @Provides fun provideOkHttp(interceptors: Set<Interceptors>) = // ... }a
  35. @Module abstract class DataModule { @Multibinds abstract fun adapters(): Set<JsonAdapter>

    @Provides fun provideMoshi(adapters: Set<JsonAdapter>) = // ... }a
  36. data class ScreenKey( val id: String, val xpId: String )a

    class ScreenPresenter @Inject constructor( screens: Map<ScreenKey, Provider<Screen>>, private val xpManager: XpManager ) { private val finalScreens = screens.filterKeys { xpManager.isEnabled(it.xpId) } }
  37. Dagger Party Tricks • github.com/ZacSweers/CatchUp • github.com/JakeWharton/sdksearch • github.com/JakeWharton/u2020 •

    Dagger Powered Plugin System post: https:// bit.ly/2T2Yqkl • Dependency Injection in a multi module project: https://bit.ly/2Gz5YFR @ZacSweers