Slide 1

Slide 1 text

Dependency injection made simple Dan Lew

Slide 2

Slide 2 text

What is a dependency?

Slide 3

Slide 3 text

A depends on B

Slide 4

Slide 4 text

• Data provider depends on database • Image Loader depends on HTTP • REST depends on HTTP • REST depends on deserializer • Login screen depends on all the above

Slide 5

Slide 5 text

What is dependency injection?

Slide 6

Slide 6 text

"Dependency Injection" is a 25-dollar term for a 5-cent concept. http://www.jamesshore.com/Blog/Dependency-Injection-Demystified.html ~James Shore

Slide 7

Slide 7 text

public class Example {
 private final Dependency dependency;
 
 public Example() {
 dependency = new Dependency();
 }
 } VS public class Example {
 private final Dependency dependency;
 
 public Example(Dependency dependency) {
 this.dependency = dependency;
 }
 }

Slide 8

Slide 8 text

public class Example {
 private final Dependency dependency;
 
 public Example() {
 dependency = new Dependency();
 }
 } VS public class Example {
 private final Dependency dependency;
 
 public Example(Dependency dependency) {
 this.dependency = dependency;
 }
 }

Slide 9

Slide 9 text

• Constructor injection public class Example {
 private final Dependency dependency;
 
 public Example(Dependency dependency) {
 this.dependency = dependency;
 }
 } • Method injection public class Example {
 private Dependency dependency;
 
 public Example() { }
 
 public void setDependency(Dependency dependency) {
 this.dependency = dependency;
 }
 }

Slide 10

Slide 10 text

View(Context context)

Slide 11

Slide 11 text

No content

Slide 12

Slide 12 text

But Why? • Share dependencies • Configure dependencies externally

Slide 13

Slide 13 text

Context wrappedContext = new ContextThemeWrapper(context, R.style.MyTheme); 
 View view = new View(wrappedContext);

Slide 14

Slide 14 text

Dependency inversion principle • High-level modules should not depend on low-level modules. Both should depend on abstractions. • Abstractions should not depend on details. Details should depend on abstractions.

Slide 15

Slide 15 text

A depends on B

Slide 16

Slide 16 text

A is coupled to B

Slide 17

Slide 17 text

No content

Slide 18

Slide 18 text

No content

Slide 19

Slide 19 text

void copy() {
 char c;
 while ((c = readKeyboard()) != -1) {
 writePrinter(c);
 }
 }

Slide 20

Slide 20 text

What if… • …You want to read from disk? enum InputDevice {
 KEYBOARD, DISK
 }
 
 void copy(InputDevice input) {
 char c;
 while (true) {
 if (input == InputDevice.KEYBOARD) {
 c = readKeyboard();
 } else if (input == InputDevice.DISK) {
 c = readDisk();
 } else {
 throw new IllegalArgumentException("Whoops!");
 }
 
 if (c == -1) {
 return;
 }
 
 writePrinter(c);
 }
 }

Slide 21

Slide 21 text

What if… • …You want to write to the monitor? enum OutputDevice {
 PRINTER, MONITOR
 }
 
 void copy(OutputDevice outputDevice) {
 char c;
 while ((c = readKeyboard()) != -1) {
 if (outputDevice == OutputDevice.PRINTER) {
 writePrinter(c);
 } else if (outputDevice == OutputDevice.MONITOR) {
 writeMonitor(c);
 } else {
 throw new IllegalArgumentException("Whoops again!");
 }
 }
 }

Slide 22

Slide 22 text

• What do we test in the read / write modules? • How do we test copy?

Slide 23

Slide 23 text

Dependency inversion to the rescue

Slide 24

Slide 24 text

No content

Slide 25

Slide 25 text

interface Reader {
 char read();
 }
 
 interface Writer {
 void write(char c);
 }
 
 void copy(Reader reader, Writer writer) {
 char c;
 while ((c = reader.read()) != -1) {
 writer.write(c);
 }
 }

Slide 26

Slide 26 text

But Why? • Share dependencies • Configure dependencies externally

Slide 27

Slide 27 text

No content

Slide 28

Slide 28 text

But Why? • Share dependencies • Configure dependencies externally • Clean separation of modules

Slide 29

Slide 29 text

No content

Slide 30

Slide 30 text

But Why? • Share dependencies • Configure dependencies externally • Clean separation of modules • Easy testing

Slide 31

Slide 31 text

No content

Slide 32

Slide 32 text

@Test
 public void testCopy() {
 TestReader reader = new TestReader("Hello, world!");
 TestWriter writer = new TestWriter();
 copy(reader, writer);
 assertEquals("Hello, world!", writer.getWritten());
 }

Slide 33

Slide 33 text

But Why? • Share dependencies • Configure dependencies externally • Clean separation of modules • Easy testing • Easy debugging

Slide 34

Slide 34 text

…Why not? • More verbose • More complex

Slide 35

Slide 35 text

No content

Slide 36

Slide 36 text

Framework Advantages • Handles injection busywork • Handles logic (e.g. singletons, lazy loading) • Unified system for dependencies

Slide 37

Slide 37 text

Frameworks • Dagger 1 • Dagger 2 • Guice • Spring • PicoContainer • …And on and on and on…

Slide 38

Slide 38 text

Dagger Directed Acyclic Graph

Slide 39

Slide 39 text

Object Graph

Slide 40

Slide 40 text

Greeter

Slide 41

Slide 41 text

// Greeter.java public class Greeter {
 @Inject public Greeter() { }
 
 public void helloWorld() {
 System.out.println("Hello, world!");
 }
 } // MyModule.java @Module(injects = Greeter.class)
 public final class MyModule { } // Retrieving Greeter ObjectGraph objectGraph = ObjectGraph.create(new MyModule()); Greeter greeter = objectGraph.get(Greeter.class); greeter.helloWorld();

Slide 42

Slide 42 text

// Greeter.java public class Greeter {
 @Inject public Greeter() { }
 
 public void helloWorld() {
 System.out.println("Hello, world!");
 }
 } // MyModule.java @Module(injects = Greeter.class)
 public final class MyModule { } // Retrieving Greeter ObjectGraph objectGraph = ObjectGraph.create(new MyModule()); Greeter greeter = objectGraph.get(Greeter.class); greeter.helloWorld();

Slide 43

Slide 43 text

// Greeter.java public class Greeter {
 @Inject public Greeter() { }
 
 public void helloWorld() {
 System.out.println("Hello, world!");
 }
 } // MyModule.java @Module(injects = Greeter.class)
 public final class MyModule { } // Retrieving Greeter ObjectGraph objectGraph = ObjectGraph.create(new MyModule()); Greeter greeter = objectGraph.get(Greeter.class); greeter.helloWorld();

Slide 44

Slide 44 text

// Greeter.java public class Greeter {
 @Inject public Greeter() { }
 
 public void helloWorld() {
 System.out.println("Hello, world!");
 }
 } // MyModule.java @Module(injects = Greeter.class)
 public final class MyModule { } // Retrieving Greeter ObjectGraph objectGraph = ObjectGraph.create(new MyModule()); Greeter greeter = objectGraph.get(Greeter.class); greeter.helloWorld();

Slide 45

Slide 45 text

// Greeter.java public class Greeter {
 @Inject public Greeter() { }
 
 public void helloWorld() {
 System.out.println("Hello, world!");
 }
 } // MyModule.java @Module(injects = Greeter.class)
 public final class MyModule { } // Retrieving Greeter ObjectGraph objectGraph = ObjectGraph.create(new MyModule()); Greeter greeter = objectGraph.get(Greeter.class); greeter.helloWorld();

Slide 46

Slide 46 text

Greeter Text

Slide 47

Slide 47 text

// Text.java
 public class Text {
 @Inject public Text() { }
 
 public String getText() {
 return "Hello, world!";
 }
 }
 // Greeter.java
 public class Greeter {
 private final Text text;
 
 @Inject public Greeter(Text text) {
 this.text = text;
 }
 
 public void sayText() {
 System.out.println(text.getText());
 }
 }

Slide 48

Slide 48 text

// Text.java
 public class Text {
 @Inject public Text() { }
 
 public String getText() {
 return "Hello, world!";
 }
 }
 // Greeter.java
 public class Greeter {
 private final Text text;
 
 @Inject public Greeter(Text text) {
 this.text = text;
 }
 
 public void sayText() {
 System.out.println(text.getText());
 }
 }

Slide 49

Slide 49 text

// Text.java
 public class Text {
 @Inject public Text() { }
 
 public String getText() {
 return "Hello, world!";
 }
 }
 // Greeter.java
 public class Greeter {
 private final Text text;
 
 @Inject public Greeter(Text text) {
 this.text = text;
 }
 
 public void sayText() {
 System.out.println(text.getText());
 }
 }

Slide 50

Slide 50 text

// Text.java
 public class Text {
 @Inject public Text() { }
 
 public String getText() {
 return "Hello, world!";
 }
 }
 // Greeter.java
 public class Greeter {
 private final Text text;
 
 @Inject public Greeter(Text text) {
 this.text = text;
 }
 
 public void sayText() {
 System.out.println(text.getText());
 }
 }

Slide 51

Slide 51 text

@Inject
 public ManyDependencies(
 Dep1 bulbasaur,
 Dep2 charmander,
 Dep3 squirtle,
 Dep4 caterpie,
 Dep5 pikachu, Dep6 slowpoke
 )

Slide 52

Slide 52 text

// Greeter.java
 public class Greeter {
 @Inject Text text;
 
 public void sayText() {
 System.out.println(text.getText());
 }
 }

Slide 53

Slide 53 text

// Greeter.java
 public class Greeter {
 @Inject Text text;
 
 public void sayText() {
 System.out.println(text.getText());
 }
 }

Slide 54

Slide 54 text

public class ManyDependencies {
 @Inject Dep1 eevee;
 @Inject Dep2 ditto;
 @Inject Dep3 snorlax;
 @Inject Dep4 articuno;
 @Inject Dep5 dragonite;
 @Inject Dep6 mew;
 }

Slide 55

Slide 55 text

Providers • Explicitly provide dependencies • Greater control over dependency • Can’t annotate dependency • Dependency inversion

Slide 56

Slide 56 text

Providers @Module(injects = Greeter.class)
 public final class MyModule {
 @Provides Text provideText() {
 return new Text("Hello, World!");
 }
 
 @Provides Greeter provideGreeter(Text text) {
 return new Greeter(text);
 }
 }

Slide 57

Slide 57 text

Providers @Module(injects = Greeter.class)
 public final class MyModule {
 @Provides Text provideText() {
 return new Text("Hello, World!");
 }
 
 @Provides Greeter provideGreeter(Text text) {
 return new Greeter(text);
 }
 }

Slide 58

Slide 58 text

Providers @Module(injects = Greeter.class)
 public final class MyModule {
 @Provides Text provideText() {
 return new Text("Hello, World!");
 }
 
 @Provides Greeter provideGreeter(Text text) {
 return new Greeter(text);
 }
 }

Slide 59

Slide 59 text

Providers @Module(injects = Greeter.class)
 public final class MyModule {
 @Provides Text provideText() {
 return new Text("Hello, World!");
 }
 
 @Provides Greeter provideGreeter(Text text) {
 return new Greeter(text);
 }
 }

Slide 60

Slide 60 text

Providing Interfaces

Slide 61

Slide 61 text

Providing Interfaces public interface Reader {
 char read();
 }
 public class KeyboardReader implements Reader {
 @Override
 public char read() { /* implementation */ }
 }
 @Module(injects = Reader.class)
 public final class MyModule {
 @Provides Reader provideReader() {
 return new KeyboardReader();
 }
 }

Slide 62

Slide 62 text

Providing Interfaces public interface Reader {
 char read();
 }
 public class KeyboardReader implements Reader {
 @Override
 public char read() { /* implementation */ }
 }
 @Module(injects = Reader.class)
 public final class MyModule {
 @Provides Reader provideReader() {
 return new KeyboardReader();
 }
 }

Slide 63

Slide 63 text

Providing Interfaces public interface Reader {
 char read();
 }
 public class KeyboardReader implements Reader {
 @Override
 public char read() { /* implementation */ }
 }
 @Module(injects = Reader.class)
 public final class MyModule {
 @Provides Reader provideReader() {
 return new KeyboardReader();
 }
 }

Slide 64

Slide 64 text

Providing Interfaces public interface Reader {
 char read();
 }
 public class KeyboardReader implements Reader {
 @Override
 public char read() { /* implementation */ }
 }
 @Module(injects = Reader.class)
 public final class MyModule {
 @Provides Reader provideReader() {
 return new KeyboardReader();
 }
 }

Slide 65

Slide 65 text

Providing Interfaces public interface Reader {
 char read();
 }
 public class KeyboardReader implements Reader {
 @Override
 public char read() { /* implementation */ }
 }
 @Module(injects = Reader.class)
 public final class MyModule {
 @Provides Reader provideReader() {
 return new KeyboardReader();
 }
 }

Slide 66

Slide 66 text

Single Module

Slide 67

Slide 67 text

Multiple Modules

Slide 68

Slide 68 text

Multiple Modules • Compile time @Module(includes = HttpModule.class)
 public final class RestModule { } • Runtime ObjectGraph.create(new RestModule(), new HttpModule()); Warning!

Slide 69

Slide 69 text

No content

Slide 70

Slide 70 text

Unused! Incomplete!

Slide 71

Slide 71 text

// HttpModule.java @Module(library = true)
 public final class HttpModule {
 @Provides OkHttp provideOkHttp() {
 return new OkHttp();
 }
 }
 // RestModule.java @Module(complete = false)
 public final class RestModule {
 @Provides Retrofit provideRetrofit(OkHttp okHttp) {
 return new Retrofit(okHttp);
 }
 }

Slide 72

Slide 72 text

// HttpModule.java @Module(library = true)
 public final class HttpModule {
 @Provides OkHttp provideOkHttp() {
 return new OkHttp();
 }
 }
 // RestModule.java @Module(complete = false)
 public final class RestModule {
 @Provides Retrofit provideRetrofit(OkHttp okHttp) {
 return new Retrofit(okHttp);
 }
 }

Slide 73

Slide 73 text

// HttpModule.java @Module(library = true)
 public final class HttpModule {
 @Provides OkHttp provideOkHttp() {
 return new OkHttp();
 }
 }
 // RestModule.java @Module(complete = false)
 public final class RestModule {
 @Provides Retrofit provideRetrofit(OkHttp okHttp) {
 return new Retrofit(okHttp);
 }
 }

Slide 74

Slide 74 text

// HttpModule.java @Module(library = true)
 public final class HttpModule {
 @Provides OkHttp provideOkHttp() {
 return new OkHttp();
 }
 }
 // RestModule.java @Module(complete = false)
 public final class RestModule {
 @Provides Retrofit provideRetrofit(OkHttp okHttp) {
 return new Retrofit(okHttp);
 }
 }

Slide 75

Slide 75 text

// HttpModule.java @Module(library = true)
 public final class HttpModule {
 @Provides OkHttp provideOkHttp() {
 return new OkHttp();
 }
 }
 // RestModule.java @Module(complete = false)
 public final class RestModule {
 @Provides Retrofit provideRetrofit(OkHttp okHttp) {
 return new Retrofit(okHttp);
 }
 }

Slide 76

Slide 76 text

// HttpModule.java @Module(library = true)
 public final class HttpModule {
 @Provides OkHttp provideOkHttp() {
 return new OkHttp();
 }
 }
 // RestModule.java @Module(complete = false)
 public final class RestModule {
 @Provides Retrofit provideRetrofit(OkHttp okHttp) {
 return new Retrofit(okHttp);
 }
 }

Slide 77

Slide 77 text

// HttpModule.java @Module(library = true)
 public final class HttpModule {
 @Provides OkHttp provideOkHttp() {
 return new OkHttp();
 }
 }
 // RestModule.java @Module(complete = false)
 public final class RestModule {
 @Provides Retrofit provideRetrofit(OkHttp okHttp) {
 return new Retrofit(okHttp);
 }
 }

Slide 78

Slide 78 text

Other Dagger Features • Singletons @Provides @Singleton Greeter provideGreeter() • Lazy injecting @Inject Lazy text; • Qualifiers @Provides @Named("Greeter1") Greeter provideGreeter()

Slide 79

Slide 79 text

No content

Slide 80

Slide 80 text

Dagger + Gradle dependencies {
 provided 'com.squareup.dagger:dagger-compiler:1.2.2'
 compile 'com.squareup.dagger:dagger:1.2.2'
 }

Slide 81

Slide 81 text

Dagger + Gradle apply plugin: 'com.neenbedankt.android-apt'
 
 dependencies {
 apt 'com.squareup.dagger:dagger-compiler:1.2.2'
 compile 'com.squareup.dagger:dagger:1.2.2'
 } https://bitbucket.org/hvisser/android-apt

Slide 82

Slide 82 text

Dagger + ProGuard • Google ProGuard config • Use Dagger 2

Slide 83

Slide 83 text

Injecting Android • Activity, Service, Fragment, etc… • Problem: We don’t control construction! • Solution: ObjectGraph.inject()

Slide 84

Slide 84 text

Injecting Activities public class MyActivity extends Activity {
 
 @Inject Dependency dependency;
 
 @Override
 protected void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 ObjectGraph objectGraph = ((MyApplication) getApplication()).getObjectGraph();
 objectGraph.inject(this);
 
 setContentView(R.layout.main);
 }
 }

Slide 85

Slide 85 text

Injecting Activities public class MyActivity extends Activity {
 
 @Inject Dependency dependency;
 
 @Override
 protected void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 ObjectGraph objectGraph = ((MyApplication) getApplication()).getObjectGraph();
 objectGraph.inject(this);
 
 setContentView(R.layout.main);
 }
 }

Slide 86

Slide 86 text

Injecting Activities public class MyActivity extends Activity {
 
 @Inject Dependency dependency;
 
 @Override
 protected void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 ObjectGraph objectGraph = ((MyApplication) getApplication()).getObjectGraph();
 objectGraph.inject(this);
 
 setContentView(R.layout.main);
 }
 }

Slide 87

Slide 87 text

u2020 • Unicode character for “dagger”: † • Injector pattern • https://github.com/JakeWharton/u2020

Slide 88

Slide 88 text

Thanks! @danlew42 danlew.net