Dagger Reflect - The circle from runtime to compile time and back again

Dagger Reflect - The circle from runtime to compile time and back again

Back in the day Java dependency injection frameworks were purely runtime based like Spring and Guava. Dagger 1 introduced a compile time component as well as compile time safety and Dagger 2 took the concept even further. Now we're going back to runtime with Dagger Reflect in an effort to improve local developer build speeds. This talk will cover the reasons for building dagger reflect, how it works under the hood, how to use it in your project as well as how it is developed.

Presented at Droidcon SF Nov 58, 2019

5f69045a2ca496221cfc624405917cdf?s=128

Nelson Osacky

November 25, 2019
Tweet

Transcript

  1. Dagger Reflect Nelson Osacky from runtime to compile time and

    back again
  2. None
  3. Me • Android for 9 years • Chocolate and Running

    • Previously at Square in SF on Square Register and Build Tools • Currently Working at SoundCloud in Berlin • Maintainer of Fladle - Gradle Plugin for Firebase Test Lab https://github.com/runningcode/fladle • Gradle Doctor - https://github.com/runningcode/gradle- doctor
  4. None
  5. Why?

  6. None
  7. What is Dependency Injection?

  8. public class Store {a private Item item; public Store()a{ item

    = new ItemImpl1(); }a }b
  9. public class Store {a private Item item; public Store( )a{

    itema=bnew ItemImpl1(); }a }b
  10. public class Store {a private Item item; public Store( Itembitem

    )a{ this.item = item; }a }b
  11. public class Store {a private Item item; public Store(Itembitem)a{ this.item

    = item; }a }b
  12. Dependency Injection in the Java world

  13. Spring

  14. Spring <bean id="item1" class="com.soundcloud.ItemImpl1" /> <bean id="store" class="com.soundcloud.Store"> <constructor-arg type="ItemImpl1"

    index="0" name="item" ref="item1" /> </bean>
  15. Java 1.5 added @Annotations

  16. Guice

  17. Guice public class Store {c private Item item; public Store(Item

    item) {d this.item = item; }a }b
  18. Guice public class Store {c private Item item; @Inject public

    Store(Item item) {d this.item = item; }a }b
  19. Dagger

  20. Dagger public class Store {c private Item item; @Inject public

    Store(Item item) {d this.item = item; }a }b
  21. Dagger @Module public class PlayerModule { @Provides public PlayerProvider providePlayerProvider(AppFeatures

    appFeatures) { return new PlayerProviderImpl(appfeatures); } }
  22. None
  23. Dagger 2

  24. @Component(includes = PlayerModule.class) public interface ApplicationComponent { }

  25. Super rough comparison (not to scale) 0 25 50 75

    100 Guice Dagger 1 Dagger 2 Compile Time Runtime
  26. None
  27. None
  28. None
  29. Dagger Reflect

  30. A reflection-based implementation of the Dagger dependency injection library for

    fast local builds.
  31. MyApplication DaggerApplicationComponent

  32. @Component public interface ApplicationComponent {b }a

  33. @Component public interface ApplicationComponent {b }a DaggerApplicationComponent .factory() .create(this)

  34. @Component public interface ApplicationComponent {b @Component.Factory interface Builder {b} }a

    DaggerApplicationComponent .factory() .create(this)
  35. @Component public interface ApplicationComponent {b void inject(MyApplication application); @Component.Factory interface

    Builder {b} }a DaggerApplicationComponent .factory() .create(this)
  36. @Component public interface ApplicationComponent {b void inject(MyApplication application); @Component.Factory interface

    Builder {b} }a DaggerApplicationComponent .factory() .create(this) .inject(this)
  37. class MyApplication : Application(), HasAndroidInjector { override fun onCreate() {

    super.onCreate() DaggerApplicationComponent .factory() .create(this) .inject(this) } }
  38. MyApplication DaggerApplicationComponent Generated by Dagger ~10,000 lines

  39. public final class DaggerApplicationComponent { private void initialize(final ApplicationModule applicationModuleParam,

    final FacebookModule facebookModuleParam, final ApiModule apiModuleParam, final AppFeaturesModule appFeaturesModuleParam, final. BaseAnalyticsModule baseAnalyticsModuleParam, final AdAnalyticsModule adAnalyticsModuleParam, final AppBoyModule appBoyModuleParam, final Application applicationParam) { this.userTopTracksFragmentSubcomponentFactoryProvider = new Provider<UserTopTracksModule_BindUserTopTracksFragment.UserTopTracksFragmentSubcomponent.Factory>() { @Override public UserTopTracksModule_BindUserTopTracksFragment.UserTopTracksFragmentSubcomponent.Factory get( ) { return new UserTopTracksFragmentSubcomponentFactory();} }; this.soundRecorderServiceSubcomponentFactoryProvider = new Provider<CreatorsModule_ProvidesSoundRecorder.SoundRecorderServiceSubcomponent.Factory>() { @Override public CreatorsModule_ProvidesSoundRecorder.SoundRecorderServiceSubcomponent.Factory get() { return new SoundRecorderServiceSubcomponentFactory();} }; this.recordFragmentSubcomponentFactoryProvider = new Provider<CreatorsModule_ProvidesRecordFragment.RecordFragmentSubcomponent.Factory>() { @Override public CreatorsModule_ProvidesRecordFragment.RecordFragmentSubcomponent.Factory get() { return new RecordFragmentSubcomponentFactory();} }; this.recordAppWidgetProviderSubcomponentFactoryProvider = new Provider<CreatorsModule_ProvidesRecordAppWidgetProvider.RecordAppWidgetProviderSubcomponent.Factory>() { @Override public CreatorsModule_ProvidesRecordAppWidgetProvider.RecordAppWidgetProviderSubcomponent.Factory get( ) { return new RecordAppWidgetProviderSubcomponentFactory();} }; this.homescreenWidgetBroadcastReceiverSubcomponentFactoryProvider = new Provider<HomescreenWidgetModule_HomescreenWidgetBroadcastReceiver.HomescreenWidgetBroadcastReceiverSubcomponent.Factory>() { @Override public HomescreenWidgetModule_HomescreenWidgetBroadcastReceiver.HomescreenWidgetBroadcastReceiverSubcomponent.Factory get( ) { return new HomescreenWidgetBroadcastReceiverSubcomponentFactory();} }; this.mainActivitySubcomponentFactoryProvider = new Provider<ActivityBuilder_BindMainActivity.MainActivitySubcomponent.Factory>() { @Override public ActivityBuilder_BindMainActivity.MainActivitySubcomponent.Factory get() { return new MainActivitySubcomponentFactory();} }; this.launcherActivitySubcomponentFactoryProvider = new Provider<ActivityBuilder_BindLauncherActivity.LauncherActivitySubcomponent.Factory>() { @Override public ActivityBuilder_BindLauncherActivity.LauncherActivitySubcomponent.Factory get() { return new LauncherActivitySubcomponentFactory();} }; this.resolveActivitySubcomponentFactoryProvider = new Provider<ActivityBuilder_BindResolveActivity.ResolveActivitySubcomponent.Factory>() { @Override public ActivityBuilder_BindResolveActivity.ResolveActivitySubcomponent.Factory get() { return new ResolveActivitySubcomponentFactory();} }; this.changeStorageLocationActivitySubcomponentFactoryProvider = new Provider<ActivityBuilder_BindChangeLocationStorageActivity.ChangeStorageLocationActivitySubcomponent.Factory>() { @Override public ActivityBuilder_BindChangeLocationStorageActivity.ChangeStorageLocationActivitySubcomponent.Factory get( ) { return new ChangeStorageLocationActivitySubcomponentFactory();} }; this.fullScreenVideoActivitySubcomponentFactoryProvider = new Provider<ActivityBuilder_BindFullScreenVideoActivity.FullScreenVideoActivitySubcomponent.Factory>() { @Override public ActivityBuilder_BindFullScreenVideoActivity.FullScreenVideoActivitySubcomponent.Factory get( ) { return new FullScreenVideoActivitySubcomponentFactory();} }; this.goOnboardingActivitySubcomponentFactoryProvider = new Provider<ActivityBuilder_BindGoOnboardingActivity.GoOnboardingActivitySubcomponent.Factory>() { @Override public ActivityBuilder_BindGoOnboardingActivity.GoOnboardingActivitySubcomponent.Factory get( ) {
  40. MyApplication DaggerApplicationComponent Generated by Dagger ~10,000 lines

  41. MyApplication DaggerApplicationComponent Generated by DaggeriReflect 20 lines long

  42. MyApplication DaggerApplicationComponent Generated by DaggeriReflect 20 lines long DaggerReflect Dagger

    Reflect Runtime
  43. MyApplication DaggerApplicationComponent DaggerReflect DaggerApplicationComponent

  44. MyApplication DaggerApplicationComponent DaggerReflect DaggerApplicationComponent

  45. MyApplication DaggerApplicationComponent Generated by DaggeriReflect DaggerReflect DaggerApplicationComponent Generated by Dagger

  46. public final class DaggerApplicationComponent {a public static ApplicationComponent.Builder factory() {b

    return DaggerReflect.factory(ApplicationComponent.Builder.class); }c }d
  47. public final class DaggerApplicationComponent {a public static ApplicationComponent.Builder factory() {b

    return DaggerReflect.factory(ApplicationComponent.Builder.class); }c }d Generated by Dagger Reflect
  48. DaggerReflect.factory(ApplicationComponent.Builder.class);

  49. DaggerReflect.factory(ApplicationComponent.Builder.class);

  50. DaggerReflect factory

  51. public final class DaggerReflect {a public static <F> F factory(Class<F>

    factoryClass) {b }c }d
  52. public final class DaggerReflect {a public static <F> F factory(Class<F>

    factoryClass) {b }c }d In Dagger Reflect Library
  53. public final class DaggerReflect {a public static <F> F factory(Class<F>

    factoryClass) {b return ComponentFactoryInvocationHandler.forComponentFactory(factoryClass); }c }d In Dagger Reflect Library
  54. ComponentFactoryInvocationHandler.forComponentFactory(factoryClass); In Dagger Reflect Library

  55. ComponentFactoryInvocationHandler In Dagger Reflect Library

  56. InvocationHandler

  57. InvocationHandler java.lang.Reflect

  58. public interface InvocationHandler { public Object invoke(Object proxy, Method method,

    Object[] args) } java.lang.Reflect
  59. InvocationHandler handles method invocations on Proxy classes

  60. Proxy classes

  61. Proxy classes Implement interfaces at runtime when they are created

  62. Create a proxy class at runtime implementing our Component Interface

  63. MyApplication DaggerApplicationComponent Generated by DaggeriReflect 20 lines long DaggerReflect Dagger

    Reflect Runtime
  64. MyApplication DaggerApplicationComponent DaggerReflect

  65. MyApplication DaggerApplicationComponent Factory DaggerApplicationComponent ComponentFactoryInvocationHandler

  66. MyApplication DaggerApplicationComponent.Factory DaggerApplicationComponent ComponentFactoryInvocationHandler

  67. ComponentFactoryInvocationHandler.forComponentFactory(factoryClass); In Dagger Reflect Library

  68. ComponentFactoryInvocationHandler forComponentFactory In Dagger Reflect Library

  69. static <F> F forComponentFactory(Class<F> factoryClass) {a requireAnnotation(factoryClass, Component.Factory.class); Class<?> componentClass

    = requireEnclosingClass(factoryClass); return newProxy( factoryClass, new ComponentFactoryInvocationHandler( componentClass, () -> ComponentScopeBuilder.buildComponent(componentClass))); }b In Dagger Reflect Library
  70. static <F> F forComponentFactory(Class<F> factoryClass) {a requireAnnotation(factoryClass, Component.Factory.class); Class<?> componentClass

    = requireEnclosingClass(factoryClass); return newProxy( factoryClass, new ComponentFactoryInvocationHandler( componentClass, () -> ComponentScopeBuilder.buildComponent(componentClass))); }b In Dagger Reflect Library
  71. static <F> F forComponentFactory(Class<F> factoryClass) {a requireAnnotation(factoryClass, Component.Factory.class); Class<?> componentClass

    = requireEnclosingClass(factoryClass); return newProxy( factoryClass, new ComponentFactoryInvocationHandler( componentClass, () -> ComponentScopeBuilder.buildComponent(componentClass))); }b In Dagger Reflect Library
  72. static <F> F forComponentFactory(Class<F> factoryClass) {a requireAnnotation(factoryClass, Component.Factory.class); Class<?> componentClass

    = requireEnclosingClass(factoryClass); return newProxy( factoryClass, new ComponentFactoryInvocationHandler( componentClass, () -> ComponentScopeBuilder.buildComponent(componentClass))); }b In Dagger Reflect Library
  73. static <F> F forComponentFactory(Class<F> factoryClass) {a requireAnnotation(factoryClass, Component.Factory.class); Class<?> componentClass

    = requireEnclosingClass(factoryClass); return newProxy( factoryClass, new ComponentFactoryInvocationHandler( componentClass, () -> ComponentScopeBuilder.buildComponent(componentClass))); }b In Dagger Reflect Library
  74. static <F> F forComponentFactory(Class<F> factoryClass) {a requireAnnotation(factoryClass, Component.Factory.class); Class<?> componentClass

    = requireEnclosingClass(factoryClass); return newProxy( factoryClass, new ComponentFactoryInvocationHandler( componentClass, () -> ComponentScopeBuilder.buildComponent(componentClass))); }b In Dagger Reflect Library
  75. static <F> F forComponentFactory(Class<F> factoryClass) {a requireAnnotation(factoryClass, Component.Factory.class); Class<?> componentClass

    = requireEnclosingClass(factoryClass); return newProxy( factoryClass, new ComponentFactoryInvocationHandler( componentClass, () -> ComponentScopeBuilder.buildComponent(componentClass))); }b In Dagger Reflect Library
  76. static <F> F forComponentFactory(Class<F> factoryClass) {a requireAnnotation(factoryClass, Component.Factory.class); Class<?> componentClass

    = requireEnclosingClass(factoryClass); return newProxy( factoryClass, new ComponentFactoryInvocationHandler( componentClass, () -> ComponentScopeBuilder.buildComponent(componentClass))); }b In Dagger Reflect Library
  77. static <F> F forComponentFactory(Class<F> factoryClass) {a requireAnnotation(factoryClass, Component.Factory.class); Class<?> componentClass

    = requireEnclosingClass(factoryClass); return newProxy( factoryClass, new ComponentFactoryInvocationHandler( componentClass, () -> ComponentScopeBuilder.buildComponent(componentClass))); }b In Dagger Reflect Library
  78. ComponentScopeBuilder.buildComponent(componentClass)) In Dagger Reflect Library

  79. ComponentScopeBuilder buildComponent In Dagger Reflect Library

  80. static ComponentScopeBuilder buildComponent(Class<?> componentClass) {a Component component = requireAnnotation(componentClass, Component.class);

    Set<Annotation> scopeAnnotation = findScopes(componentClass.getAnnotations()); return create(component.modules(), component.dependencies(), scopeAnnotation, null); }b In Dagger Reflect Library
  81. static ComponentScopeBuilder buildComponent(Class<?> componentClass) {a Component component = requireAnnotation(componentClass, Component.class);

    Set<Annotation> scopeAnnotation = findScopes(componentClass.getAnnotations()); return create(component.modules(), component.dependencies(), scopeAnnotation, null); }b In Dagger Reflect Library
  82. static ComponentScopeBuilder buildComponent(Class<?> componentClass) {a Component component = requireAnnotation(componentClass, Component.class);

    Set<Annotation> scopeAnnotation = findScopes(componentClass.getAnnotations()); return create(component.modules(), component.dependencies(), scopeAnnotation, null); }b In Dagger Reflect Library
  83. static ComponentScopeBuilder buildComponent(Class<?> componentClass) {a Component component = requireAnnotation(componentClass, Component.class);

    Set<Annotation> scopeAnnotation = findScopes(componentClass.getAnnotations()); return create(component.modules(), component.dependencies(), scopeAnnotation, null); }b In Dagger Reflect Library
  84. None
  85. Dagger Reflect Development

  86. reflect reflect-compiler integration-tests codegen Project Gradle Setup

  87. reflect Reflection based implementation of Dagger

  88. reflect reflect-compiler integration-tests codegen Project Gradle Setup

  89. reflect-compiler Generates the glue to link your code to the

    reflection based dagger implementation
  90. reflect reflect-compiler integration-tests codegen Project Gradle Setup

  91. integration-tests All the tests!

  92. integration-tests Make sure we do the same thing as Dagger!

  93. integration-tests Make sure we do the same thing as Dagger!

  94. @Test public void componentProvider() {a ComponentProvider component = backend.create(ComponentProvider.class); assertThat(component.string()).isEqualTo("foo");

    }b
  95. @Test public void componentProvider() {a ComponentProvider component = backend.create(ComponentProvider.class); assertThat(component.string()).isEqualTo("foo");

    }b @Component(modules = ComponentProvider.Module1.class) interface ComponentProvider { String string(); @Module abstract class Module1 { @Provides static String string() { return "foo"; } } }
  96. @Test public void componentProvider() {a ComponentProvider component = backend.create(ComponentProvider.class); assertThat(component.string()).isEqualTo("foo");

    }b
  97. backend.create(ComponentProvider.class);

  98. @RunWith(Parameterized.class) public final class IntegrationTest { @Parameters(name = "{0}") public

    static Object[] parameters() { return Backend.values(); } @Parameter public Backend backend = backend.create(ComponentProvider.class); }
  99. Backend.values();

  100. Backend values();

  101. enum Backend {a REFLECT, CODEGEN }e

  102. enum Backend {a abstract <C> C create(Class<C> componentClass); REFLECT, CODEGEN

    }e
  103. enum Backend {a abstract <C> C create(Class<C> componentClass); REFLECT {b

    @Overridea <F>aFacreate(Class<F>acomponentClass) { returnaDaggerReflect.create(componentClass); }c },d CODEGEN }e
  104. enum Backend {a abstract <C> C create(Class<C> componentClass); REFLECT {b

    @Overridea <F>aFacreate(Class<F>acomponentClass) { returnaDaggerReflect.create(componentClass); }c },d CODEGEN { @Override <F> F create(Class<F> componentClass) { return DaggerCodegen.create(componentClass); } }; }e
  105. backend.create(ComponentProvider.class);

  106. @RunWith(Parameterized.class) public final class IntegrationTest { @Parameters(name = "{0}") public

    static Object[] parameters() { return Backend.values(); } @Parameter public Backend backend = backend.create(ComponentProvider.class); }
  107. backend.create(ComponentProvider.class);

  108. @Test public void componentProvider() { ComponentProvider component = backend.create(ComponentProvider.class); assertThat(component.string()).isEqualTo("foo");

    }
  109. How to contribute

  110. Try it and write tests

  111. @Test public void justInTimeGenericNested()a{ ignoreReflectionBackend(); JustInTimeGenericNested component = backend.create(JustInTimeGenericNested.class); assertThat(component.thing()).isNotNull();

    }b
  112. pu()a{ ignoreReflectionBackend(); Jus asserTNu }

  113. private void ignoreReflectionBackend() { assumeTrue("Not yet implemented for reflection backend",

    backend != Backend.REFLECT); }
  114. @Test public void justInTimeGenericNested()a{ ignoreReflectionBackend(); JustInTimeGenericNested component = backend.create(JustInTimeGenericNested.class); assertThat(component.thing()).isNotNull();

    }b
  115. @Test public void justInTimeGenericNested() { JustInTimeGenericNested component = backend.create(JustInTimeGenericNested.class); assertThat(component.thing()).isNotNull();

    }
  116. integration-tests Make sure we do the same thing as Dagger!

  117. integration-tests Run the Dagger tests on Dagger Reflect upstream

  118. test.filter { // dagger-reflect does not produce the exact same

    behavior as dagger-compiler for @Reusable. excludeTest 'dagger.functional.ReusableTest', null // TODO reflect bug! Need something like ByteBuddy for proxying classes at runtime. excludeTest 'dagger.functional.builder.BuilderBindsInstanceParameterTest', null // TODO reflect bug! Generics don't work well. excludeTest 'dagger.functional.GenericTest', 'complexGenerics' }
  119. Usage

  120. Make sure to use @Component.Builder or @Component.Factory

  121. @Component interface ApplicationComponent {a }d

  122. @Component interface ApplicationComponent {a @Component.Factory interface Factory {b }c }d

  123. class MyApplication : Application(), HasAndroidInjector { override fun onCreate() {

    super.onCreate() DaggerApplicationComponent .factory() .create(this) .inject(this) } }
  124. Make sure Dagger Annotations have Runtime Retention

  125. @Qualifier public @interface EventGatewayBaseUrl {a }b

  126. @Qualifier @Retention(RetentionPolicy.RUNTIME) public @interface EventGatewayBaseUrl {a }b

  127. @Qualifier @Retention(RetentionPolicy.RUNTIME) public @interface EventGatewayBaseUrl { } Structural Based Search

    Inspection
  128. Swap Dagger for Dagger Reflect

  129. implementation deps.dagger.dagger kapt deps.dagger.compiler

  130. implementation deps.dagger.dagger kapt deps.dagger.compiler private fun useDaggerReflect() : Boolean {

    return properties["dagger.reflect"] == "true") }
  131. private fun useDaggerReflect() : Boolean { return properties["dagger.reflect"] == "true")

    }
  132. implementation deps.dagger.dagger if ( useDaggerReflect() ) { implementation deps.dagger.reflectRuntime kapt

    deps.dagger.daggerReflectCompiler } else { kapt deps.dagger.compiler }q
  133. implementation deps.dagger.dagger if (useDaggerReflect()) { implementation deps.dagger.reflectRuntime kapt deps.dagger.daggerReflectCompiler }

    else { kapt deps.dagger.compiler }q
  134. Swap Dagger for Dagger Reflect

  135. Swap Dagger for Dagger Reflect in every module

  136. implementationadeps.dagger.dagger if (useDaggerReflect()) {c implementation deps.dagger.reflectRuntime kapt deps.dagger.daggerReflectCompiler }celse {a

    kapt deps.dagger.compiler }b
  137. implementationadeps.dagger.dagger implementationbdeps.dagger.daggerAndroid if (useDaggerReflect()) {c implementation deps.dagger.reflectRuntime kapt deps.dagger.daggerReflectCompiler }celse

    {a kapt deps.dagger.compiler kapt deps.dagger.daggerAndroidCompiler }b
  138. Swap Dagger for Dagger Reflect in every module

  139. Swap Dagger for Dagger Reflect in every module and for

    Dagger Android
  140. Swap Dagger for Dagger Reflect in every module and for

    Dagger Android and for every new module
  141. implementationadeps.dagger.dagger implementationbdeps.dagger.daggerAndroid if (useDaggerReflect()) {c implementation deps.dagger.reflectRuntime kapt deps.dagger.daggerReflectCompiler }celse

    {a kapt deps.dagger.compiler kapt deps.dagger.daggerAndroidCompiler }b
  142. implementationadeps.dagger.dagger implementationbdeps.dagger.daggerAndroid lintChecks deps.dagger.daggerReflectLint if (useDaggerReflect()) {c implementation deps.dagger.reflectRuntime kapt

    deps.dagger.daggerReflectCompiler }celse {a kapt deps.dagger.compiler kapt deps.dagger.daggerAndroidCompiler }b
  143. Swap Dagger for Dagger Reflect in every module and for

    Dagger Android and for every new module and don’t forget the lint check
  144. It’s high maintenance

  145. None
  146. apply plugin: 'com.soundcloud.delect'

  147. Dagger Reflect Gradle Plugin

  148. apply plugin: 'com.soundcloud.delect'

  149. buildscript { classpath 'com.soundcloud.delect:delect-plugin:0.1.0' } apply plugin: 'com.soundcloud.delect'

  150. echo "dagger.reflect=true" > ~/.gradle/gradle.properties (For local dev buils)

  151. echo "dagger.reflect=true" > ~/.gradle/gradle.properties

  152. Delect

  153. implementation deps.dagger.dagger if (useDaggerReflect()) { implementation deps.dagger.reflectRuntime kapt deps.dagger.daggerReflectCompiler }

    else { kapt deps.dagger.compiler }
  154. configurations.all config@ {a dependencies.all {b //bIf we depend on the

    daggerbruntime, also add the daggerbreflect runtime. ifb(group == daggerGroupId && name == "dagger") {c dependencies {d add(this@config.name, "$com.jakewharton.dagger:dagger-reflect:${extension.daggerReflectVersion}") }e }f }g resolutionStrategy {h dependencySubstitution {o // Substitute dagger compiler for dagger reflect compiler. substitute( module("$com.google.dagger:dagger-compiler“) ).apply { with(module("$com.jakewharton.dagger:dagger-reflect-compiler:${extension.daggerReflectVersion}")) because("We want to build faster.") }p substitute(module("$com.google.dagger:dagger-android-processor")) .with(module("$com.jakewharton.dagger:dagger-reflect-compiler:${extension.daggerReflectVersion}")) }q }r }s
  155. configurations.all config@ {a dependencies.all {b //bIf we depend on the

    daggerbruntime, also add the daggerbreflect runtime. ifb(group == daggerGroupId && name == "dagger") {c dependencies {d add(this@config.name, "$com.jakewharton.dagger:dagger-reflect:${extension.daggerReflectVersion}") }e }f }g }s
  156. configurations.all config@ {a dependencies.all {b //bIf we depend on the

    daggerbruntime, also add the daggerbreflect runtime. ifb(group == daggerGroupId && name == "dagger") {c dependencies {d add(this@config.name, "$com.jakewharton.dagger:dagger-reflect:${extension.daggerReflectVersion}") }e }f }g }s
  157. configurations.all config@ {a dependencies.all {b //bIf we depend on the

    daggerbruntime, also add the daggerbreflect runtime. ifb(group == daggerGroupId && name == "dagger") {c dependencies {d add(this@config.name, "$com.jakewharton.dagger:dagger-reflect:${extension.daggerReflectVersion}") }e }f }g resolutionStrategy {h dependencySubstitution {o // Substitute dagger compiler for dagger reflect compiler. substitute( module("$com.google.dagger:dagger-compiler") ).apply { with(module("$com.jakewharton.dagger:dagger-reflect-compiler:${extension.daggerReflectVersion}")) because("We want to build faster.") }p substitute(module("$com.google.dagger:dagger-android-processor")) .with(module("$com.jakewharton.dagger:dagger-reflect-compiler:${extension.daggerReflectVersion}")) }q }r }s
  158. configurations.all config@ {a resolutionStrategy {h dependencySubstitution {o // Substitute dagger

    compiler for dagger reflect compiler. substitute( module("$com.google.dagger:dagger-compiler") ).apply { with(module("$com.jakewharton.dagger:dagger-reflect-compiler:${extension.daggerReflectVersion}")) because("We want to build faster.") }p }q }r }s
  159. configurations.all config@ {a resolutionStrategy {h dependencySubstitution {o // Substitute dagger

    compiler for dagger reflect compiler. substitute( module("$com.google.dagger:dagger-compiler") ).apply { with(module("$com.jakewharton.dagger:dagger-reflect-compiler:${extension.daggerReflectVersion}")) because("We want to build faster.") }p substitute(module("$com.google.dagger:dagger-android-processor")) .with(module("$com.jakewharton.dagger:dagger-reflect-compiler:${extension.daggerReflectVersion}")) }q }r }s
  160. configurations.all config@ {a resolutionStrategy {h dependencySubstitution {o // Substitute dagger

    compiler for dagger reflect compiler. substitute( module("$com.google.dagger:dagger-compiler") ).apply { with(module("$com.jakewharton.dagger:dagger-reflect-compiler:${extension.daggerReflectVersion}")) because("We want to build faster.") }p substitute(module("$com.google.dagger:dagger-android-processor")) .with(module("$com.jakewharton.dagger:dagger-reflect-compiler:${extension.daggerReflectVersion}")) }q }r }s
  161. configurations.all config@ {a dependencies.all {b //bIf we depend on the

    daggerbruntime, also add the daggerbreflect runtime. ifb(group == daggerGroupId && name == "dagger") {c dependencies {d add(this@config.name, "$com.jakewharton.dagger:dagger-reflect:${extension.daggerReflectVersion}") }e }f }g resolutionStrategy {h dependencySubstitution {o // Substitute dagger compiler for dagger reflect compiler. substitute( module("$com.google.dagger:dagger-compiler") ).apply { with(module("$com.jakewharton.dagger:dagger-reflect-compiler:${extension.daggerReflectVersion}")) because("We want to build faster.") }p substitute(module("$com.google.dagger:dagger-android-processor")) .with(module("$com.jakewharton.dagger:dagger-reflect-compiler:${extension.daggerReflectVersion}")) }q }r }s
  162. The current state of Dagger Reflect

  163. • Slower startup time • Lazy reflection (doesn’t scan entire

    app at startup) • Different behavior than production • Not all Dagger features are supported yet • Poor error messages
  164. Future of Dagger Reflect

  165. • Match Dagger with Features • Improve Error Messages •

    Improve Runtime Performance • Kotlin Dagger Reflect
  166. Relative build performance J genating J only K generating K

    only K & J generating K & J Time https://eng.uber.com/measuring-kotlin-build-performance/
  167. At SoundCloud

  168. At SoundCloud • Long running 20% project • Been using

    it since July • Using a fork of Dagger Reflect with our supported features • Devs can opt in using dagger.reflect=true
  169. None
  170. Dagger Reflect a0.1.0a

  171. Dagger Reflect a0.1.0a 0.1.0 Delect

  172. None
  173. How do you make people opt in?

  174. None
  175. None
  176. None
  177. None
  178. None
  179. None
  180. None
  181. None
  182. None
  183. None
  184. Let’s talk build times

  185. None
  186. None
  187. None
  188. None
  189. None
  190. None
  191. The longer the build the longer the Dagger time

  192. None
  193. None
  194. What’s the real world performance?

  195. Average build time for last 4 weeks of builds

  196. Average build time for last 4 weeks of builds

  197. 58.89s Dagger 19% faster builds 47.55s Dagger Reflect Average Build

    Time - Last 4 Weeks
  198. • Thanks to Riccardo for testing Delect and help with

    open sourcing • Thanks to my team for testing out Dagger Reflect • Thanks to SoundCloud for letting me have the time to work on this • Thanks Jake Wharton for reviewing my code and building and releasing 0.1.0
  199. More information • DIY: Build your own dependency injection library

    - Pierre- Yves Ricau - https://academy.realm.io/posts/android- pierre-yves-ricau-build-own-dependency-injection/ • Dagger Reflect: https://github.com/jakewharton/dagger- reflect • Dagger Reflect Gradle Plugin: https://github.com/ soundcloud/delect • A brief history of dependency injection http://mvpjava.com/ brief-history-dependency-injection/
  200. Questions? nelson@osacky.com Let’s talk build speeds and productivity osacky.com