Slide 1

Slide 1 text

Dependency Injection on Android frameworks & internals Mobius 2017 St.Petersburg

Slide 2

Slide 2 text

+stephane nicolas @PreusslerBerlin Senior Android Dev @ Groupon OSS: Dart, TP, BoundBox, … Lead Android Dev @ Viacom Google Developer Expert Combined ~40 years of Java Coding

Slide 3

Slide 3 text

No content

Slide 4

Slide 4 text

Viacom

Slide 5

Slide 5 text

● Dependency Injection ● History of DI libs on Android ● What is reflection ? ● Dagger Vs. Toothpick ● Annotation Processing ● Conclusion Plan

Slide 6

Slide 6 text

Dependency Injection?

Slide 7

Slide 7 text

Dependency Inversion Inversion of Control Dependency Injection ? ? ?

Slide 8

Slide 8 text

The Dependency Inversion Principle High level entities should not depend on low level details.

Slide 9

Slide 9 text

Inversion of Control Who initiates a message Hollywood's Law: don't call me, I'll call you.

Slide 10

Slide 10 text

Inversion of Control Stop using new

Slide 11

Slide 11 text

private final Tracker tracker = new GoogleAnalyticsTracker(); @Override protected void onCreate(Bundle state) { ... tracker.trackStarted(); }

Slide 12

Slide 12 text

Inversion of Control Common implementations: ● Factory ● Service Locator ● Dependency Injection https://martinfowler.com/articles/dipInTheWild.html#YouMeanDependencyInversionRight

Slide 13

Slide 13 text

Inversion of Control Common implementations: ● Factory tracker = Factory.createTracker() ● Service Locator ● Dependency Injection https://martinfowler.com/articles/dipInTheWild.html#YouMeanDependencyInversionRight

Slide 14

Slide 14 text

Inversion of Control Common implementations: ● Factory ● Service Locator tracker = Locator.get(Tracker.class) ● Dependency Injection https://martinfowler.com/articles/dipInTheWild.html#YouMeanDependencyInversionRight

Slide 15

Slide 15 text

Inversion of Control Common implementations: ● Factory ● Service Locator ● Dependency Injection @Inject Tracker tracker; https://martinfowler.com/articles/dipInTheWild.html#YouMeanDependencyInversionRight

Slide 16

Slide 16 text

Dependency Injection ● Field injection ● Constructor injection ● Setter injection ● Method injection

Slide 17

Slide 17 text

Dependency Injection ● Field injection @Inject Tracker tracker; ● Constructor injection ● Setter injection ● Method injection

Slide 18

Slide 18 text

Dependency Injection ● Field injection ● Constructor injection @Inject MyClass(Tracker tracker) {...} ● Setter injection ● Method injection

Slide 19

Slide 19 text

A brief history of DI libs for Java / Android Revolutions are the locomotives of history. Karl Marx

Slide 20

Slide 20 text

A brief history of DI libs for Java / Android March 8, 2007: Guice 1.0 is released. Guice is annotation based to perform DI which is a huge improvement over former frameworks. It uses reflection to access annotations, create instances and inject stuff.

Slide 21

Slide 21 text

A brief history of DI libs for Java / Android October 2009: JSR 330 final draft released. ● Guice is de facto the first implementation of the JSR 330

Slide 22

Slide 22 text

A brief history of DI libs for Java / Android May 2010: RoboGuice was launched ! ● First DI lib on Android. ● Based on Guice (reflection). ● Supports view bindings, extras, events, etc..

Slide 23

Slide 23 text

A brief history of DI libs for Java / Android June 2012: Dagger is started ! ● The goal is to create a compile time implementation of JSR 330.

Slide 24

Slide 24 text

A brief history of DI libs for Java / Android May 2013: Dagger 1.0.0 is launched ! ● Compile time implementation of JSR 330. No more reflection or very very limited. ● Annotation processing at compile time. ● Generated code is used to assign members & create instances.

Slide 25

Slide 25 text

A brief history of DI libs for Java / Android April 2015: Dagger 2.0.0 is launched ! ● Faster than Dagger 1 ● Easier error messages

Slide 26

Slide 26 text

A brief history of DI libs for Java / Android October 2016: Toothpick 1.0.0 ! ● As fast as the daggers. ● Hybrid compile time and runtime. ● More flexible, simpler, amazing test support.

Slide 27

Slide 27 text

A brief history of DI libs for Java / Android Many libs now : ● Light saber (kotlin) ● Proton ● Feather ● Tiger (Dagger 2 improvements)

Slide 28

Slide 28 text

What is reflection ? Why is bad on Android ?

Slide 29

Slide 29 text

What is reflection ?

Slide 30

Slide 30 text

What is reflection ? ● uses OOO concepts to represent objects, classes, methods, constructors, fields, annotations, etc. ● is an API to get a view of runtime java objects. ● is standard java. ● is relatively easy to use.

Slide 31

Slide 31 text

What is reflection ? MyClass object = Myclass.class .getConstructors()[0].newInstance(); Method setter = Myclass.class .getDeclaredMethod(“setFoo”, {String.class}); setter.setAccessible(true); setter.invoke(object, “set via reflection”); Field foo = MyClass.class.getDeclaredField(“foo”); foo.setAccessible(true); String value = foo.get(object);

Slide 32

Slide 32 text

Why is reflection slow (on Android) ? On a PC JVM ○ Reflection calls are cached after 15 calls ○ They are then transformed into normal code (JIT) ○ 15 is parametrized by sun.reflect.inflation system property

Slide 33

Slide 33 text

Why is reflection slow (on Android) ? ● On Android Dalvik ○ Reflection calls are not cached, no JIT ○ The dex format is not efficient for reflection ○ There was a bug that slowed down access to annotations by reflection (before GingerBread) ● On Android Art ○ In Nougat, reflection calls are now cached using JIT ○ But data structure of odex is still slow ○ Bug is solved

Slide 34

Slide 34 text

What is slow in reflection on Android ?

Slide 35

Slide 35 text

What is slow in reflection on Android ?

Slide 36

Slide 36 text

What is slow in reflection on Android ?

Slide 37

Slide 37 text

Dagger Vs Toothpick

Slide 38

Slide 38 text

Dagger vs Toothpick ● Usage ● Setup ● Scopes ● Tests ● Performance

Slide 39

Slide 39 text

Dagger vs Toothpick: Round 1: Usage @Inject Tracker tracker; D agger

Slide 40

Slide 40 text

Dagger vs Toothpick: Round 1: Usage DaggerDependencies_AppComponent .builder() .build() .inject(this) D agger

Slide 41

Slide 41 text

Dagger vs Toothpick: Round 1: Usage DaggerDependencies_AppComponent .builder() .baseModule(new BaseModule(context)) .build() .inject(this) D agger

Slide 42

Slide 42 text

Dagger vs Toothpick: Round 1: Usage @Inject Tracker tracker; Toothpick

Slide 43

Slide 43 text

Dagger vs Toothpick: Round 1: Usage openScope("APPLICATION").inject(this); Toothpick

Slide 44

Slide 44 text

Dagger vs Toothpick: Round 1: Usage Scope scope = openScope("APPLICATION"); scope.installModules(new BaseModule(context)); scope.inject(this); Toothpick

Slide 45

Slide 45 text

Dagger vs Toothpick: Round 2: Setup @Module class BaseModule { BaseModule(Application context){} … @Provides public Tracker provideTracker() { return new GoogleTracker(); } } D agger

Slide 46

Slide 46 text

Dagger vs Toothpick: Round 2: Setup @Component(modules = {BaseModule.class}) interface AppComponent { void inject(MyActivity activity); } D agger

Slide 47

Slide 47 text

Dagger vs Toothpick: Round 2: Setup class BaseModule extends Module { public BaseModule(Application context){ bind(Tracker.class) .to(GoogleTracker.class); Toothpick

Slide 48

Slide 48 text

Dagger vs Toothpick: Round 3: Scopes Activity Scope Application Scope Application singletons Activity singletons

Slide 49

Slide 49 text

Dagger vs Toothpick: Round 3: Scopes @Scope @Retention(RUNTIME) public @interface ActivityScope { } D agger

Slide 50

Slide 50 text

Dagger vs Toothpick: Round 3: Scopes @ActivityScope @Subcomponent(modules = {ScopeModule.class}) interface ScopeComponent { void inject(ScopeActivity activity); } D agger

Slide 51

Slide 51 text

Dagger vs Toothpick: Round 3: Scopes @Module static class ScopeModule { ... @Provides @ActivityScope public Activity provideActivity() { return activity; } } D agger

Slide 52

Slide 52 text

Dagger vs Toothpick: Round 3: Scopes @Component(modules = {BaseModule.class}) interface AppComponent { void inject(LonelyActivity activity); ScopeComponent plus(ScopeModule module); } D agger

Slide 53

Slide 53 text

That’s annotation porn!

Slide 54

Slide 54 text

Dagger vs Toothpick: Round 3: Scopes @Override public void onCreate() { super.onCreate(); DaggerDependencies_AppComponent.builder() .baseModule(new BaseModule(this)).build() .plus(new ScopeModule(this)) .inject(this) D agger

Slide 55

Slide 55 text

Dagger vs Toothpick: Round 3: Scopes Scope scope = openScope("APPLICATION", "MY_ACTIVITY") scope.installModules(new ScopeModule(this))); Toothpick

Slide 56

Slide 56 text

Dagger vs Toothpick: Round 3: Scopes Scope scope = openScope("APPLICATION", "MY_ACTIVITY") scope.installModules(new ScopeModule(this))); .inject(this) Toothpick

Slide 57

Slide 57 text

Dagger vs Toothpick: Round 3: Scopes Scope verification ● Dagger: compile-time ● Toothpick: runtime

Slide 58

Slide 58 text

Dagger vs Toothpick: Round 4: Tests D agger

Slide 59

Slide 59 text

Dagger vs Toothpick: Round 4: Tests @Mock Tracker tracker; class TestModule extends Dependencies.BaseModule { @Provides public Tracker provideTracker() { return tracker; } } } D agger

Slide 60

Slide 60 text

Dagger vs Toothpick: Round 4: Tests MyApplication.set( DaggerDependencies_AppComponent .builder() .baseModule( new TestModule()).build()); D agger

Slide 61

Slide 61 text

Dagger vs Toothpick: Round 4: Tests @Mock Tracker tracker; @Rule public ToothPickRule toothPickRule = new ToothPickRule( this, "APPLICATION_SCOPE"); Toothpick

Slide 62

Slide 62 text

Dagger vs Toothpick: Round 4: Tests @Mock Tracker tracker; @Mock Navigator navigator; @Mock Logger logger; class TestModule extends Dependencies.BaseModule { @Provides public Tracker provideTracker() { return tracker; } @Provides public Navigator provideNavigator() { return tracker; } @Provides public Logger provideLogger() { return logger; } } D agger

Slide 63

Slide 63 text

Dagger vs Toothpick: Round 4: Tests @Mock Tracker tracker; @Mock Navigator navigator; @Mock Logger logger; @Rule public ToothPickRule toothPickRule = new ToothPickRule( this, "APPLICATION_SCOPE"); Toothpick

Slide 64

Slide 64 text

Dagger vs Toothpick: Round 5: Performance Costs of creating a Component/Scope ● Dagger 1: 20 ms ● Dagger 2: 22 ms ● Toothpick: 1 ms

Slide 65

Slide 65 text

Dagger vs Toothpick: Round 5: Performance Costs of usage with 1000 injections: ● Dagger 1: 33 ms ● Dagger 2: 31 ms ● Toothpick: 35 ms

Slide 66

Slide 66 text

Dagger vs Toothpick: Round 5: Performance Costs of usage with 6400 injections: ● Dagger 1: 45 ms ● Dagger 2: 42 ms ● Toothpick: 66 ms

Slide 67

Slide 67 text

Dagger vs Toothpick: Round 5: Performance Costs of usage:

Slide 68

Slide 68 text

Dagger & Toothpick Let’s talk about the internals Let’s talk about Annotation Processing

Slide 69

Slide 69 text

What is annotation processing ?

Slide 70

Slide 70 text

What is annotation processing ?

Slide 71

Slide 71 text

What is annotation processing ? Annotation processing: ● is an API to get a view of java classes before they are compiled. ● is standard java. ● uses different concepts to represent classes (mirrors & TypeElements), methods & constructors (ExecutableElements), constructors, fields (Elements), annotations, etc. ● is not easy to use, not easy to debug, not easy to memorize and learn. ● annotation processors can generate code and/or resources.

Slide 72

Slide 72 text

What is annotation processing ? TypeElement enclosingElement = (TypeElement) element.getEnclosingElement(); Set modifiers = executableElement.getModifiers(); if (modifiers.contains(PRIVATE)) { ... }

Slide 73

Slide 73 text

Which code is generated ? @Module class SlidesModule { @Provides DisplayOut displayOut(Resolution resolution){ return new UcsbDisplayOut(resolution); } } D agger

Slide 74

Slide 74 text

Which code is generated ? @Generated public final class SlidesModule_DisplayOutFactory implements Factory { private final SlidesModule module; private final Provider resolutionProvider; public static SlidesModule_DisplayOutFactory create( SlidesModule module, Provider resolutionProvider) {..} @Override public DisplayOut get() { return module.displayOut(resolutionProvider.get()}; } } D agger

Slide 75

Slide 75 text

Which code is generated ? Toothpick class UcsbDisplayOut { @Inject UcsbDisplayOut(Resolution resolution) { …. } }

Slide 76

Slide 76 text

Which code is generated ? Toothpick public final class UcsbDisplayOut$$Factory implements Factory { @Override public UcsbDisplayOut createInstance(Scope scope) { Resolution resolution = scope.getInstance(Resolution.class); return new UcsbDisplayOut(resolution); } }

Slide 77

Slide 77 text

Which code is generated ? Basically, both libs generate: ● Factories to create instances ● MemberInjectors to assign members Moreover Dagger generates code for: ● Modules, Components And Tootpick can also generate code for: ● Registries

Slide 78

Slide 78 text

Which code is generated ? Dagger: ● generates a static graph, ● generated code only calls generated code ● very efficient ● but all wiring is static ● hard to modify for testing D agger

Slide 79

Slide 79 text

Which code is generated ? Toothpick: ● generates a dynamic graph ● generated code calls runtime code to get the bindings ● a bit less efficient ● but more flexible ● easier to change for testing. Toothpick

Slide 80

Slide 80 text

Alternatives to reflection & annotation Processing ?

Slide 81

Slide 81 text

Conclusion: ● Dagger provides compile-time scope verification ● Dagger might be a little more efficient ● Toothpick avoids boilerplate code ● Toothpick is easier for testing ● Toothpick scopes are more clear Dagger vs Toothpick: Overall

Slide 82

Slide 82 text

+stephane nicolas @PreusslerBerlin Thank you, see you tomorrow