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 :)
Dagger 2
in Android
(from the top down)
Vince Mi
twitter: @v_mi
github: vinc3m1
email: q@uber.com
http://t.uber.com/dagger2codepath
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.
Audience Prerequisites
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.
TL;DR
Separation of Configuration & Usage
Maintainability, testability, mockability, low coupling, easy singletons, less boilerplate*
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
Extremely brief history of Dagger
Dagger 1 - Square
Dagger 2 - Google
Usage
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)
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.
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.
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
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
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.
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
Where do we get the Component?
public MyComponent getComponent() { return component; }
((MyApp) this.getApplicationContext()).getComponent().inject(this);
MyApp.java
MyActivity.java
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
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
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
Configuration
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
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
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
Defining a Component
@Component
public interface MyAppComponent {
void inject(MyActivity myActivity);
}
MyAppComponent.java
Actual Implementation is generated by Dagger 2.
Just an interface:
Depending on a Module
@Component(
modules = {
MyAppModule.class,
}
)
public interface MyAppComponent {
void inject(MyActivity myActivity);
}
MyAppComponent.java
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
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
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
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
Bringing it all together
public class MyApp extends Application {
private MyComponent component;
@Override public void onCreate() {
component = ???
}
public MyComponent getComponent() { … }
}
MyApp.java
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
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
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
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
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
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
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
Scopes
@Scope
@Retention(RetentionPolicy.RUNTIME)
public @interface PerApp { }
@Scope
@Retention(RetentionPolicy.RUNTIME)
public @interface PerActivity { }
PerApp.java
PerActivity.java
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
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) {
}
}
Q & A
Resources