1 of 43

Find an newer version here: http://t.uber.com/dagger2middleout

The ideas are the same, but takes a slightly different path in the initial “usage” section.

Tweet me which one you like more :)

@v_mi

2 of 43

Dagger 2

in Android

(from the top down)

Vince Mi

twitter: @v_mi

github: vinc3m1

email: q@uber.com

http://t.uber.com/dagger2codepath

3 of 43

Goal of this presentation

To explain the reasons and concepts behind Dependency Injection, specifically Dagger 2 in in the context of Android.

To understand the big picture so you’ll know where to look for the answers.

4 of 43

Audience Prerequisites

  • You know how to write a basic Android App
  • Nice to have:
    • You’re familiar with ButterKnife
    • You’ve written a fairly large or growing Andorid App
    • You’ve configured and used networking stacks, imaging libraries, persistent data...

5 of 43

Wikipedia

Dependency injection is a software design pattern in which one or more dependencies (or services) are injected, or passed by reference, into a dependent object (or client) and are made part of the client's state. The pattern separates the creation of a client's dependencies from its own behavior, which allows program designs to be loosely coupled and to follow the dependency inversion and single responsibility principles.

6 of 43

TL;DR

Separation of Configuration & Usage

Maintainability, testability, mockability, low coupling, easy singletons, less boilerplate*

7 of 43

Configuration vs Usage

SharedPreferences

Image Loader

(Picasso)

Network Client

(OkHttp)

API Interface

(Retrofit)

Http Disk Cache

Image Disk Cache

SessionManager

(Auth tokens)

Activities

Fragments

Views

Objects

8 of 43

Extremely brief history of Dagger

Dagger 1 - Square

  • Object Graph
  • Code Generation
  • Some Reflection

Dagger 2 - Google

  • Components
  • Code Generation
  • No Reflection

9 of 43

Usage

10 of 43

Butterknife Analogy

TextView mTitleText;

TextView mBodyText;

TextView mIconText;

// in constructor

mTitleText =

(TextView) findViewById(R.id.title)

mBodyText =

(TextView) findViewById(R.id.body)

mFooterText =

(ImageView) findViewById(R.id.icon)

@InjectView(R.id.title)

TextView mTitleText;

@InjectView(R.id.body)

TextView mBodyText;

@InjectView(R.id.icon)

ImageView mIcon;

// in constructor

Butterknife.inject(this, viewGroup)

11 of 43

Butterknife Analogy

TextView mTitleText;

TextView mBodyText;

TextView mIconText;

// in constructor

mTitleText =

(TextView) findViewById(R.id.title)

mBodyText =

(TextView) findViewById(R.id.body)

mFooterText =

(ImageView) findViewById(R.id.icon)

@InjectView(R.id.title)

TextView mTitleText;

@InjectView(R.id.body)

TextView mBodyText;

@InjectView(R.id.icon)

ImageView mIcon;

// in constructor

Butterknife.inject(this, viewGroup)

Take all fields with annotation, look in the ViewGroup and find the view by id, cast to expected type, set field.

12 of 43

Butterknife Analogy

ViewGroup

Object

findViewById

@InjectView

Take all fields with annotation, look in the ViewGroup and find the view by id, cast to expected type, set field.

13 of 43

Configuration vs Usage

SharedPreferences

Image Loader

(Picasso)

Network Client

(OkHttp)

API Interface

(Retrofit)

Http Disk Cache

Image Disk Cache

SessionManager

(Auth tokens)

Activities

Fragments

Views

Objects

@Inject

14 of 43

Dagger Usage

Component

Object

“find object

by type”

@Inject

Take all fields with annotation, look in the Component and find the object by type (or create it), and set the field.

FKA ObjectGraph

15 of 43

Dagger Usage

@Inject Picasso mPicasso;

@Inject SessionManager mSessionManager;

@Inject Toolbar mToolbar;

@Inject UberApi mUberApi;

// in onCreate()

getComponent().inject(this);

Take all fields with annotation, look in the Component and find the object by type (or create it), and set the field.

16 of 43

Where do we get the Component?

public class MyApp extends Application {

private MyAppComponent component;

@Override public void onCreate() {

component = ???

}

public MyAppComponent getComponent() {

return component;

}

}

<application

android:name=".MyApp"

…>

<activity … />

</application>

AndroidManifest.xml

MyApp.java

17 of 43

Where do we get the Component?

public MyComponent getComponent() { return component; }

((MyApp) this.getApplicationContext()).getComponent().inject(this);

MyApp.java

MyActivity.java

18 of 43

Where do we get the Component?

MyApp.get(this).getComponent().inject(this);

public static MyApp get(Context context) {

return ((MyApp) context.getApplicationContext());

}

public MyComponent getComponent() { return component; }

MyActivity.java

MyApp.java

19 of 43

Where do we get the Component?

public static MyComponent getComponent(Context context) {

return ((MyApp) context.getApplicationContext()).component;

}

MyApp.getComponent(this).inject(this);

MyApp.java

MyActivity.java

20 of 43

Where do we get the Component?

public static MyComponent getComponent(Context context) {

return ((MyApp) context.getApplicationContext()).component;

}

MyApp.getComponent(this).inject(this);

MyApp.getComponent(getActivity()).inject(this);

MyApp.java

MyActivity.java

MyFragment.java

21 of 43

Configuration

22 of 43

Configuration vs Usage

SharedPreferences

Image Loader

(Picasso)

Network Client

(OkHttp)

API Interface

(Retrofit)

Http Disk Cache

Image Disk Cache

SessionManager

(Auth tokens)

Activities

Fragments

Views

Objects

23 of 43

Modules & Components

Activities

Fragments

Views

Objects

Networking

Module

Storage

Module

Application

Module

Application

Component

.inject()

FKA ObjectGraph

“the graph”

Network Client

API Interface

Disk Cache

SharedPreferences

Application

24 of 43

Modules & More Components

.inject()

.inject()

Activities

Fragments

Views

Objects

Networking

Module

Storage

Module

Application

Module

Application

Component

Activity

Module

Activity

Component

Activities

Fragments

Views

Objects

FKA ObjectGraph

FKA “sub-graph”

Toolbar

Shared State

25 of 43

Defining a Component

@Component

public interface MyAppComponent {

void inject(MyActivity myActivity);

}

MyAppComponent.java

Actual Implementation is generated by Dagger 2.

Just an interface:

26 of 43

Depending on a Module

@Component(

modules = {

MyAppModule.class,

}

)

public interface MyAppComponent {

void inject(MyActivity myActivity);

}

MyAppComponent.java

27 of 43

Defining a Module

@Module public class MyAppModule {

private final MyApp app;

public MyAppModule(MyApp app) {

this.app = app;

}

@Provides MyApp provideMyApp() {

return app;

}

}

MyAppModule.java

28 of 43

Defining a Module

@Module public class MyAppModule {

private final MyApp app;

public MyAppModule(MyApp app) {

this.app = app;

}

@Provides @Singleton MyApp provideMyApp() {

return app;

}

}

MyAppModule.java

29 of 43

Defining a Module

@Module public class MyAppModule {

private final MyApp app;

public MyAppModule(MyApp app) {

this.app = app;

}

@Provides @PerApp MyApp provideMyApp() {

return app;

}

}

MyAppModule.java

30 of 43

Providing Objects

@Module public class ApiModule {

@Provides @Singleton RestAdapter provideRestAdapter(Endpoint endpoint, Client

client, Converter converter, ApiHeaders headers) {

return new RestAdapter.Builder()

.setClient(client)

.setEndpoint(endpoint)

.setConverter(converter)

.setRequestInterceptor(headers)

.setLogLevel(BuildConfig.DEBUG

? RestAdapter.LogLevel.FULL

: RestAdapter.LogLevel.NONE)

.build();

}

}

MyAppModule.java

31 of 43

Bringing it all together

public class MyApp extends Application {

private MyComponent component;

@Override public void onCreate() {

component = ???

}

public MyComponent getComponent() { … }

}

MyApp.java

32 of 43

Bringing it all together

public class MyApp extends Application {

private MyComponent component;

@Override public void onCreate() {

component = DaggerMyAppComponent.builder()

.myAppModule(new MyAppModule(this))

.build();

}

public MyComponent getComponent() { … }

}

MyApp.java

33 of 43

The Component Builder

@Singleton

@Component(

modules = MyAppModule.class

)

public interface MyAppComponent {

void inject(MyApp myApp);

}

DaggerMyAppComponent.builder()

.myAppModule(new MyAppModule(this))

.build();

MyAppComponent.java

MyApp.java

34 of 43

The Component Builder

@Singleton

@Component(

modules = {

MyAppModule.class

DataModule.class,

NetworkModule.class

}

)

public interface MyAppComponent {

void inject(MyApp myApp);

}

DaggerMyAppComponent.builder()

.myAppModule(new MyAppModule(this))

.dataModule(new DataModule())

.networkModule(new NetworkModule())

.build();

MyAppComponent.java

MyApp.java

35 of 43

Modules & Components

Activities

Fragments

Views

Objects

Networking

Module

Storage

Module

Application

Module

Application

Component

.inject()

FKA ObjectGraph

“the graph”

Network Client

API Interface

Disk Cache

SharedPreferences

Application

36 of 43

Component Dependencies

.inject()

.inject()

Activities

Fragments

Views

Objects

Networking

Module

Storage

Module

Application

Module

Application

Component

Activity

Module

Activity

Component

Activities

Fragments

Views

Objects

FKA ObjectGraph

FKA “sub-graph”

Toolbar

Shared State

37 of 43

Component Dependencies

@Component(

dependencies =

MyAppComponent.class,

modules =

MyActivityModule.class

)

public interface MyActivityComponent {

void inject(MyActivity myActivity);

}

DaggerMyAppComponent.builder()

.myAppComponent(

MyApp.getComponent(this))

.myActivityModule(

new MyActivityModule(this))

.build();

MyActivityComponent.java

MyActivity.java

38 of 43

Component Dependencies

@Component(

dependencies = MyAppComponent.class,

modules = MyActivityModule.class

)

public interface MyActivityComponent {

void inject(MyActivity myActivity);

void inject(MyFragment myFragment);

}

DaggerMyActivityComponent.builder()

.myAppComponent(MyApp.getComponent(this))

.myActivityModule(new MyActivityModule(this))

.build();

@Component(

modules = {

MyAppModule.class

}

)

public interface MyAppComponent {

void inject(MyApp myApp);

void inject(MyActivity myActivity);

MyApp myApp();

SharedPreferences sharedPrefs();

}

MyActivityComponent.java

MyActivity.java

MyAppComponent.java

39 of 43

Scopes

@Scope

@Retention(RetentionPolicy.RUNTIME)

public @interface PerApp { }

@Scope

@Retention(RetentionPolicy.RUNTIME)

public @interface PerActivity { }

PerApp.java

PerActivity.java

40 of 43

Scopes

@Scope

@Retention(RetentionPolicy.RUNTIME)

public @interface PerApp { }

@Scope

@Retention(RetentionPolicy.RUNTIME)

public @interface PerActivity { }

@PerApp

@Component(…)

public interface MyAppComponent {

@PerApp @Provides SharedPrefs…

@PerActivity

@Component(…)

public interface MyAactivityComponent {

@PerActivity @Provides Toolbar…

PerApp.java

PerActivity.java

41 of 43

Constructor Injection

@Provides MyThing provideMyThing(

SharedPreferences sp,

Context context) {

return new MyThing(sp, context);

}

@PerApp

class MyThing {

private final SharedPRefs…

private final Context ...

@Inject public MyThing(

SharedPreferences sp,

Context context) {

}

}

42 of 43

Q & A

43 of 43

Resources