Slide 1

Slide 1 text

Dagger Party Tricks @ZacSweers

Slide 2

Slide 2 text

Dagger?

Slide 3

Slide 3 text

Dagger Party Tricks

Slide 4

Slide 4 text

4ish Dagger Party Tricks

Slide 5

Slide 5 text

Deferred Initialization

Slide 6

Slide 6 text

No content

Slide 7

Slide 7 text

No content

Slide 8

Slide 8 text

No content

Slide 9

Slide 9 text

@Provides fun provideRetrofit(): Retrofit { return Retrofit.Builder() .baseUrl("https://example.com") .build() }a

Slide 10

Slide 10 text

@Provides fun provideClient(): OkHttpClient { return OkHttpClient.Builder() .build() }b @Provides fun provideRetrofit(client: OkHttpClient): Retrofit { return Retrofit.Builder() .client(client) .build() }a

Slide 11

Slide 11 text

@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

Slide 12

Slide 12 text

@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

Slide 13

Slide 13 text

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

Slide 14

Slide 14 text

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

Slide 15

Slide 15 text

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

Slide 16

Slide 16 text

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

Slide 17

Slide 17 text

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

Slide 18

Slide 18 text

Lazy!

Slide 19

Slide 19 text

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

Slide 20

Slide 20 text

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 )e 100-150ms!

Slide 21

Slide 21 text

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 )e 100-150ms! retrofit.get().create(...)

Slide 22

Slide 22 text

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 )e 100-150ms! retrofit.get().create(...)

Slide 23

Slide 23 text

@Provides fun provideRetrofit(client: OkHttpClient): Retrofit { return Retrofit.Builder() .client(client) .build() }a

Slide 24

Slide 24 text

@Provides fun provideRetrofit(client: OkHttpClient): Retrofit { return Retrofit.Builder() .callFactory(client) .build() }a

Slide 25

Slide 25 text

@Provides fun provideRetrofit(client: OkHttpClient): Retrofit { return Retrofit.Builder() .callFactory(client) .build() }a Call.Factory

Slide 26

Slide 26 text

@Provides fun provideRetrofit(client: OkHttpClient): Retrofit { return Retrofit.Builder() .callFactory(object : Call.Factory by client {}) .build() }a

Slide 27

Slide 27 text

@Provides fun provideRetrofit(client: OkHttpClient): Retrofit { return Retrofit.Builder() .callFactory { client.newCall(it) } .build() }a

Slide 28

Slide 28 text

@Provides fun provideRetrofit(client: Lazy): Retrofit { return Retrofit.Builder() .callFactory { client.get().newCall(it) } .build() }a

Slide 29

Slide 29 text

@Provides fun provideRetrofit(client: Lazy): Retrofit { return Retrofit.Builder() .callFactory { client.get().newCall(it) } .build() }a

Slide 30

Slide 30 text

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 )e 100-150ms! retrofit.get().create(...)

Slide 31

Slide 31 text

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 )e 100-150ms! retrofit.get().create(...) Main thread!

Slide 32

Slide 32 text

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): Retrofit { return Retrofit.Builder() .callFactory { client.get().newCall(it) } .build() }a @Inject constructor( retrofit: Retrofit )e 100-150ms! Background thread

Slide 33

Slide 33 text

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): Retrofit { return Retrofit.Builder() .callFactory { client.get().newCall(it) } .build() }a @Inject constructor( retrofit: Retrofit )e 100-150ms! Background thread

Slide 34

Slide 34 text

Modularizing

Slide 35

Slide 35 text

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

Slide 36

Slide 36 text

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

Slide 37

Slide 37 text

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

Slide 38

Slide 38 text

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

Slide 39

Slide 39 text

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

Slide 40

Slide 40 text

@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

Slide 41

Slide 41 text

@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

Slide 42

Slide 42 text

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

Slide 43

Slide 43 text

class Seasoning @Inject constructor() class Taco @Inject constructor(seasoning: Seasoning)

Slide 44

Slide 44 text

class Taco @Inject constructor(seasoning: Seasoning) Food subproject Feature A

Slide 45

Slide 45 text

class Taco @Inject constructor(seasoning: Seasoning) Food subproject Feature A class Taco_Factory

Slide 46

Slide 46 text

class Taco @Inject constructor(seasoning: Seasoning) Food subproject Feature A class Taco_Factory Feature B class Taco_Factory

Slide 47

Slide 47 text

class Taco @Inject constructor(seasoning: Seasoning) Food subproject Feature A class Taco_Factory Feature B class Taco_Factory App

Slide 48

Slide 48 text

class Taco @Inject constructor(seasoning: Seasoning) Food subproject Feature A class Taco_Factory Feature B class Taco_Factory App

Slide 49

Slide 49 text

class Taco @Inject constructor(seasoning: Seasoning) Food subproject Feature A class Taco_Factory Feature B App

Slide 50

Slide 50 text

Internal APIs

Slide 51

Slide 51 text

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

Slide 52

Slide 52 text

@Qualifier annotation class InternalApi

Slide 53

Slide 53 text

@Qualifier private annotation class InternalApi

Slide 54

Slide 54 text

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

Slide 55

Slide 55 text

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

Slide 56

Slide 56 text

Multibindings

Slide 57

Slide 57 text

Multibindings • Set - @IntoSet, @ElementsIntoSet • Map - @IntoMap, @MapKey, etc

Slide 58

Slide 58 text

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

Slide 59

Slide 59 text

@Module class FoodModule { @Provides fun provideSpicy() = SpicySeasoning() @Provides fun provideElPaso() = ElPasoSeasoning() @Provides fun provideTaco( spicy: SpicySeasoning, elpaso: ElPasoSeasoning ) = Taco(setOf(spicy, elpaso)) }a

Slide 60

Slide 60 text

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

Slide 61

Slide 61 text

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

Slide 62

Slide 62 text

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

Slide 63

Slide 63 text

@Module abstract class FoodModule { @Multibinds abstract fun seasonings(): Set @Provides fun provideTaco(seasonings: Set) = Taco(seasonings) }a

Slide 64

Slide 64 text

@Module abstract class NetworkModule { @Multibinds abstract fun interceptors(): Set @Provides fun provideOkHttp(interceptors: Set) = // ... }a

Slide 65

Slide 65 text

@Module abstract class DataModule { @Multibinds abstract fun adapters(): Set @Provides fun provideMoshi(adapters: Set) = // ... }a

Slide 66

Slide 66 text

@Module abstract class LoggingModule { @Multibinds abstract fun trees(): Set }a

Slide 67

Slide 67 text

@Module abstract class PetsScreenModule { @Multibinds abstract fun categories(): Set }a

Slide 68

Slide 68 text

@Module abstract class PetsScreenModule { @Multibinds abstract fun categories(): Map }a

Slide 69

Slide 69 text

@Module abstract class PetsScreenModule { @Multibinds abstract fun categories(): Map> }a

Slide 70

Slide 70 text

@Module abstract class PetsScreenModule { @Multibinds abstract fun categories(): Map> }a

Slide 71

Slide 71 text

No content

Slide 72

Slide 72 text

data class ScreenKey( val id: String )a

Slide 73

Slide 73 text

data class ScreenKey( val id: String, val xpId: String )a

Slide 74

Slide 74 text

data class ScreenKey( val id: String, val xpId: String )a class ScreenPresenter @Inject constructor( screens: Map>, private val xpManager: XpManager ) { private val finalScreens = screens.filterKeys { xpManager.isEnabled(it.xpId) } }

Slide 75

Slide 75 text

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