Slide 1

Slide 1 text

Dependency Injection Building Testable Mobile Applications with JACOB TABAK @JACOBTABAK

Slide 2

Slide 2 text

A dependency of a thing is something that it needs to do its job.

Slide 3

Slide 3 text

Dependency Injection is a pattern that separates the creation of dependencies from the objects that need them.

Slide 4

Slide 4 text

No content

Slide 5

Slide 5 text

Building a city is like building software.

Slide 6

Slide 6 text

public interface City {
 Water water();
 Electricity electricity();
 Education education();
 }

Slide 7

Slide 7 text

public class MyCity implements City {
 private final WaterTower waterTower;
 private final CoalPowerPlant coalPowerPlant;
 
 public MyCity() {
 this.waterTower = new WaterTower();
 this.coalPowerPlant = new CoalPowerPlant();
 }
 
 @Override public Water water() {
 return waterTower.water();
 }
 @Override public Electricity electricity() {
 return coalPowerPlant.electricity();
 }
 @Override public Education education() { return null; }
 }

Slide 8

Slide 8 text

No content

Slide 9

Slide 9 text

public class MyCity implements City {
 private final WaterTower waterTower;
 private final CoalPowerPlant coalPowerPlant;
 private final ElementarySchool elementarySchool;
 
 public MyCity() {
 this.waterTower = new WaterTower();
 this.coalPowerPlant = new CoalPowerPlant();
 this.elementarySchool = new ElementarySchool();
 }
 
 @Override public Water water() { return waterTower.water(); }
 @Override public Electricity electricity() {
 return coalPowerPlant.electricity();
 }
 @Override public Education education() { return elementarySchool.education(); }
 }

Slide 10

Slide 10 text

No content

Slide 11

Slide 11 text

public class MyCity implements City {
 private final WaterSource waterSource;
 private final PowerPlant powerPlant;
 private final SchoolSystem schoolSystem;
 
 public MyCity() {
 this.waterSource = new WaterTower();
 this.powerPlant = new SolarPowerPlant();
 this.schoolSystem = new ElementarySchool();
 }
 
 @Override public Water water() {
 return waterSource.water();
 }
 @Override public Electricity electricity() {
 return powerPlant.electricity();
 }
 @Override public Education education() {
 return schoolSystem.education();
 }
 }

Slide 12

Slide 12 text

Dependency Injection is a pattern that separates the creation of dependencies from the objects that need them.

Slide 13

Slide 13 text

public class GenericCity implements City {
 private final WaterSource waterSource;
 private final PowerPlant powerPlant;
 private final SchoolSystem schoolSystem;
 
 public GenericCity(WaterSource waterSource, PowerPlant powerPlant, SchoolSystem schoolSystem) {
 this.waterSource = waterSource;
 this.powerPlant = powerPlant;
 this.schoolSystem = schoolSystem;
 }
 
 @Override public Water water() {
 return waterSource.water();
 }
 
 @Override public Electricity electricity() {
 return powerPlant.electricity();
 }
 
 @Override public Education education() {
 return schoolSystem.education();
 }
 } This isn’t new!

Slide 14

Slide 14 text

No content

Slide 15

Slide 15 text

Teacher firstGradeTeacher = new Teacher("John Smith");
 Teacher secondGradeTeacher = new Teacher("Jane Doe");
 Headmaster headmaster = new Headmaster("Dorothy Sanders");
 Faculty faculty = new Faculty(headmaster, Arrays.asList(firstGradeTeacher, secondGradeTeacher));
 Subject scienceSubject = new Subject("Science");
 Subject mathSubject = new Subject("Math");
 Subject englishSubject = new Subject("English");
 Curriculum curriculum = new Curriculum( Arrays.asList(scienceSubject, mathSubject, englishSubject));
 ElementarySchool elementarySchool = new ElementarySchool(faculty, curriculum);

Slide 16

Slide 16 text

A Brief History Spring, Guice, Dagger 1, Dagger 2

Slide 17

Slide 17 text

Spring Framework 2002

Slide 18

Slide 18 text


 
 
 
 
 
 
 
 
 
 
 
 
 
 


Slide 19

Slide 19 text

Pros • Initialization Ordering • Only define dependencies once Cons • Verbose XML • No compile-time validation • Poor IDE integration • No traceability • Reflection-based API Spring Framework

Slide 20

Slide 20 text

Guice Google, 2006

Slide 21

Slide 21 text

public class GenericCity implements City {
 private final WaterSource waterSource;
 private final PowerPlant powerPlant;
 private final SchoolSystem schoolSystem;
 
 @Inject public GenericCity(
 WaterSource waterSource,
 PowerPlant powerPlant,
 SchoolSystem schoolSystem
 ) {
 this.waterSource = waterSource;
 this.powerPlant = powerPlant;
 this.schoolSystem = schoolSystem;
 }
 
 @Override public Water water() {
 return waterSource.water();
 }
 
 @Override public Electricity electricity() {
 return powerPlant.electricity();
 }
 
 @Override public Education education() {
 return schoolSystem.education();
 }
 }

Slide 22

Slide 22 text

Concrete classes can be injected automatically. What about abstract classes & interfaces?

Slide 23

Slide 23 text

public class SmallCityModule extends AbstractModule {
 @Provides WaterSource provideWaterSource() { return new WaterTower(); }
 @Provides PowerPlant providePowerPlant() { return new CoalPowerPlant(); }
 @Provides SchoolSystem provideSchoolSystem() { return new ElementarySchool(); }
 } public class LargeCityModule extends AbstractModule {
 @Provides WaterSource provideWaterSource() { return new PumpingStation(); }
 @Provides PowerPlant providePowerPlant() { return new NuclearPowerPlant(); }
 @Provides SchoolSystem provideSchoolSystem() { return new PublicSchoolSystem(); }
 }

Slide 24

Slide 24 text

–Friendly Guice Runtime Error No implementation for com.example.PowerPlant was bound while locating com.example.PowerPlant for parameter 2 at com.example.GenericCity.(GenericCity.java.9) while locating com.example.GenericCity

Slide 25

Slide 25 text

Pros • Don’t need to duplicate logic between XML and Java • In-line, annotation-based config • Automatically inject concrete types with no additional configuration Cons • No compile-time validation • Poor traceability • Slow reflection-based API Google Guice

Slide 26

Slide 26 text

Dagger 1 Square, 2012

Slide 27

Slide 27 text

@Module(
 includes = { /* other modules */ },
 addsTo = { /* other module */ },
 complete = true,
 library = false,
 injects = { MainActivity.class }
 )
 public class LargeCityModule extends AbstractModule {
 @Provides WaterSource provideWaterSource() { return new PumpingStation(); }
 @Provides PowerPlant providePowerPlant() { return new NuclearPowerPlant(); }
 @Provides SchoolSystem provideSchoolSystem() { return new PublicSchoolSystem(); }
 }

Slide 28

Slide 28 text

Pros • Partial compile-time graph validation • Improved traceability • Objects instantiated without reflection • Flexible model (overrides) Cons • Graph composition happens at runtime • Generated code can be hard to read • Does not work with Proguard Dagger 1

Slide 29

Slide 29 text

Dagger 2 Google, 2015

Slide 30

Slide 30 text

@Component(modules = LargeCityModule.class)
 public interface LargeCityComponent {
 // Optional - Access dependencies directly
 WaterSource waterSource();
 PowerPlant powerPlant();
 SchoolSystem schoolSystem();
 
 // Contract for field injection
 void inject(MainActivity activity);
 } public class MainActivity extends Activity {
 @Override
 protected void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 setContentView(R.layout.activity_main);
 LargeCityComponent component = DaggerLargeCityComponent.create();
 component.inject(this);
 }
 }

Slide 31

Slide 31 text

Dagger 2 Limitations

Slide 32

Slide 32 text

@Module(
 complete = false,
 library = true
 )
 public class ApiModule {
 @Provides @Singleton Endpoint provideEndpoint() {
 return Endpoints.newFixedEndpoint("https://api.github.com");
 }
 }
 
 @Module(
 complete = false,
 library = true,
 overrides = true
 )
 public class DebugApiModule {
 @Provides @Singleton Endpoint provideEndpoint(StringPreference apiEndpointPref) {
 return Endpoints.newFixedEndpoint(apiEndpointPref.get());
 }
 }

Slide 33

Slide 33 text

Dagger 1 or Dagger 2? Dagger 1 More flexibility Simpler build integration Dagger 2 Better performance Compile-time graph validation Proguard-friendly

Slide 34

Slide 34 text

Usage

Slide 35

Slide 35 text

public class City {
 private final WaterSource waterSource;
 private final PowerPlant powerPlant;
 @Inject public City(WaterSource waterSource, PowerPlant powerPlant) {
 this.waterSource = waterSource;
 this.powerPlant = powerPlant;
 } @Override public Water water() { return waterSource.water(); }
 @Override public Electricity electricity() { return powerPlant.electricity(); }
 @Override public Education education() { return schoolSystem.education(); }
 } Constructor Injection (for most classes)

Slide 36

Slide 36 text

public class MainActivity extends Activity {
 @Inject OkHttpClient okHttpClient;
 
 @Override
 protected void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 setContentView(R.layout.activity_main);
 Injector.obtain(getApplicationContext()).inject(this);
 }
 } Field Injection (for activities/fragments)

Slide 37

Slide 37 text

public class MyCustomView extends FrameLayout {
 private final Picasso picasso;
 
 public MyCustomView(Context context) {
 super(context);
 picasso = Injector.obtain(context).getPicasso();
 }
 } Method Injection

Slide 38

Slide 38 text

@Provides @Singleton Picasso providePicasso(Application application) {
 return Picasso.with(application);
 }
 
 @Provides DateTime provideCurrentTime() {
 return new DateTime();
 } Dependency Scope

Slide 39

Slide 39 text

@Inject Provider dateTimeProvider; DateTime dateTime1 = dateTimeProvider.get();
 DateTime dateTime2 = dateTimeProvider.get(); Provider Injection

Slide 40

Slide 40 text

@Inject Lazy lazyDateTimeProvider; DateTime lazyTime = lazyDateTimeProvider.get(); Lazy Injection

Slide 41

Slide 41 text

@Module
 class UserModule {
 @Provides @Named("username") String providerUsername(SharedPreferences preferences) {
 return preferences.getString("username", null);
 }
 
 @Provides @Named("password") String providePassword(SharedPreferences preferences) {
 return preferences.getString("password", null);
 }
 } class LoginActivity extends Activity {
 @Inject @Named("username") String username;
 @Inject @Named("password") String password;
 } Qualifiers

Slide 42

Slide 42 text

Unit Testing JUnit, Mockito, Android Studio

Slide 43

Slide 43 text

Full Unit Testing support added in Android Studio 1.1

Slide 44

Slide 44 text

Simulates objects that mimic the behavior of real objects in controlled ways

Slide 45

Slide 45 text

public class ContentStatTests {
 @Mock SmsReader smsReader;
 @Mock MediaStoreReader mediaStoreReader;
 ContentStatClient statClient;
 
 @Before
 public void initialize() {
 MockitoAnnotations.initMocks(this);
 statClient = new ContentStatClient(mediaStoreReader, smsReader);
 }
 
 @Test
 public void testContentCount() {
 when(smsReader.getSmsCount()).thenReturn(1);
 when(mediaStoreReader.getPhotoCount()).thenReturn(3);
 when(mediaStoreReader.getVideoCount()).thenReturn(5);
 assertThat(statClient.getCount(), equalTo(9));
 }
 }

Slide 46

Slide 46 text

UI Testing Espresso

Slide 47

Slide 47 text

onView(withId(R.id.name_field))
 .perform(typeText("Steve"));
 onView(withId(R.id.greet_button))
 .perform(click());
 onView(withText("Hello Steve!"))
 .check(matches(isDisplayed())); Use Espresso to write concise, beautiful, and reliable Android UI tests like this: • Small, easy-to-learn API • Optimally fast: never use sleep()

Slide 48

Slide 48 text

public class ClockActivityTest {
 @Inject Clock clock;
 
 @Before
 public void setUp() {
 MyApplication app = (MyApplication) getApplication();
 TestComponent component = DaggerTestComponent.create();
 app.setComponent(component);
 component.inject(this);
 }
 
 @Test
 public void testClockAtNoon() {
 when(clock.now()).thenReturn(new LocalTime().withHourOfDay(12).withMinuteOfHour(0));
 getActivity();
 onView(withId(R.id.date_view)).check(matches(withText("12:00")));
 }
 }
 
 @Module
 class MockClockModule {
 @Provides Clock provideClock() {
 return mock(Clock.class);
 }
 }
 
 @Component(modules = MockClockModule.class)
 interface TestClockComponent extends ClockComponent {
 void inject(ClockActivityTest testClass);
 }

Slide 49

Slide 49 text

u2020 Dependency Injection in Practice

Slide 50

Slide 50 text

Never put debug code or back doors in your production app.

Slide 51

Slide 51 text

u2020 Debug Drawer

Slide 52

Slide 52 text

AppContainer (DrawerLayout) Content View (android.R.id.content) activity_main.xml

Slide 53

Slide 53 text

public class TraditionalActivity extends Activity { @Override protected void onCreate(Bundle state) {
 super.onCreate(state);
 setContentView(R.layout.activity_main);
 } } Traditional Activity Layout public class U2020Activity extends Activity { @Inject AppContainer appContainer;
 @Override protected void onCreate(Bundle state) {
 super.onCreate(state); Injector.obtain(getApplication()).inject(this); ViewGroup container = appContainer.bind(this); getLayoutInflater().inflate( R.layout.activity_main, container);
 } } u2020 Activity Layout

Slide 54

Slide 54 text

Intent Capturing

Slide 55

Slide 55 text

Scalpel http://github.com/jakewharton/scalpel

Slide 56

Slide 56 text

Questions JACOB TABAK @JACOBTABAK