Sword fighting with Dagger GDG-NYC Jan 2016
-
Upload
mike-nakhimovich -
Category
Technology
-
view
763 -
download
1
Transcript of Sword fighting with Dagger GDG-NYC Jan 2016
Sword Fighting with DaggerMike Nakhimovich
Android Engineer NY Times
What is Dagger?
Dagger offers an alternative way to instantiate
and manage your objects through
Dependency Injection
Dagger 2 open sourced by Google
Dagger 1 – Square
Guice – Google (Dagger v.0)
Compile Time Annotation Processing (Fast)
Superpowers
Lazy
Singleton
Provider
Object Scoping (Application/Session/Activity)
Why Dagger?
In the Olden Days...
Old Waypublic class RedditView {
private RedditPresenter presenter;
public RedditView(...) {
Gson gson = new GsonBuilder().create();
RedditApi redditApi = new Retrofit.Builder()
.addConverterFactory(GsonConverterFactory.create(gson))
.build().create(RedditApi.class);
RedditDAO redditDAO = new RedditDAO(redditApi);
presenter=new RedditPresenter(redditDAO);
}
Externalize Object Creation
@Provides
Gson provideGson() {
}
Create a Provider
return new GsonBuilder().create();
}
ModulesA Module is a part of your application that provides
implementations.
@Module public class DataModule {
}
@Provides Gson provideGson() {
return gsonBuilder.create();}
Dependencies@Provides methods can have their own dependencies
@Provides
RedditApi provideRedditApi(Gson gson) {
return new Retrofit.Builder()
.addConverterFactory(
GsonConverterFactory.create(gson))
.build().create(RedditApi.class);
}
Provides not necessaryYou can also annotate constructors directly to register
public class RedditDAO {
@Inject
public RedditDAO(RedditApi api) {
super();
}
}
Provided by Module
Consuming Dependencies
ComponentsA component is a part of your application that consumes
functionality. Built from Module(s)
@Component( modules = {DataModule.class})
public interface AppComponent {
void inject(RedditView a);
}
Register with ComponentActivities/Services/Views register with an instance of a
component
public RedditView(Context context) {
getComponent().inject(this);
}
Injection Fun
Now you can inject any dependencies
managed by the component.
As a field As a Constructor Param
Instantiating Dependencies Old WayRedditPresenter presenter;
public RedditView(Context context) {
super(context);
Gson gson = new GsonBuilder().create();
RedditApi redditApi = new Retrofit.Builder()
.addConverterFactory(GsonConverterFactory.create(gson))
.build()
.create(RedditApi.class);
RedditDAO redditDAO = new RedditDAO(redditApi);
presenter=new RedditPresenter(redditDAO);
}
Once RedditView registers itself with a component,
Dagger will satisfy all managed dependencies with
@Inject annotations
@Inject RedditPresenter presenter;
public RedditView(...) {getComponent().inject(this);
}
Instantiating Dependencies Dagger Way
How does Dagger do it?A static DaggerAppComponent.builder() call creates a Builder instance;
Builder creates an DaggerAppComponent instance;
DaggerAppComponent creates a RedditView_MembersInjector instance;
RedditView_MembersInjectorr uses RedditPresenter_Factory to
instantiate RedditPresenter and injects it into RedditView.
Just as fast as hand written code but with fewer errors
Generated Code@Generated("dagger.internal.codegen.ComponentProcessor")public final class RedditPresenter_Factory implements Factory<RedditPresenter> {private final MembersInjector<RedditPresenter> membersInjector;
public RedditPresenter_Factory(MembersInjector<RedditPresenter> membersInjector) { assert membersInjector != null;this.membersInjector = membersInjector;
}
@Overridepublic RedditPresenter get() { RedditPresenter instance = new RedditPresenter();membersInjector.injectMembers(instance);return instance;
}
public static Factory<RedditPresenter> create(MembersInjector<RedditPresenter> membersInjector) { return new RedditPresenter_Factory(membersInjector);
}}
Dependency ChainThe Presenter has its own dependency
public class RedditPresenter {
@Inject RedditDAO dao;
}
Dependency ChainWhich has its own dependency
public class RedditDAO {
private final RedditApi api;
@Inject
public RedditDAO(RedditApi api) {
this.api = api;
}
Dagger Eliminates “Passing Through” Constructor Arguments
Old WayRedditPresenter presenter;
public RedditView(...) {
super(context);
Gson gson = new GsonBuilder().create();
RedditApi redditApi = new Retrofit.Builder()
.addConverterFactory(GsonConverterFactory.create(gson))
.build()
.create(RedditApi.class);
RedditDAO redditDAO = new RedditDAO(redditApi);
presenter=new RedditPresenter(redditDAO);
}
Dagger Eliminates “Passing Through” Constructor Arguments
Dagger Way:Inject ONLY the dependencies each
object needs
Type of Injections
Dagger has a few types of injection:
Direct
Lazy
Provider
Direct
When instance is created also create the
dependency.
public class RedditPresenter {
@Inject RedditDAO dao;
}
Lazy
Do not instantiate until .get() is called.
public class RedditPresenter {
@Inject Lazy<RedditDAO> dao;
}
Useful to keep startup time down
ProviderProvider<T> allows you to inject multiple
instances of same object by calling .get()
public class RedditPresenter {
@Inject Provider<RedditDAO>
dao;
}
Singletons Old Waypublic class MainActivity extends Activity {
SharedPreferences pref;
Gson gson;
ServerAPI api;
onCreate(...) {
MyApp app = (MyApp)getContext().getApplicationContext();
pref = app.getSharedPreferences();
gson = app.getGson();
api = app.getApi();
Why is MYApp managing all singletons? :-(
Singletons Dagger Way
Define objects to be Singletons
@Singleton @Provides
Gson provideGson() {
return gsonBuilder.create();
}
Singletons Dagger Way
Define objects to be Singletons
@Singleton
public class RedditDAO{
@Inject
public RedditDAO() {
}
Singleton Management
@Singleton = Single instance per Component
@Singleton
@Component( modules = {DataModule.class})
public interface AppComponent {
void inject(RedditView a);
}
Scopes
Singleton is a scope annotation
@Scope
@Documented
@Retention(RUNTIME)
public @interface Singleton {}
We can create custom scope annotations
Subcomponent
Creating a Subcomponent with a scope
@Subcomponent(modules = {
ActivityModule.class})
@ActivityScope
public interface ActivityComponent
Subcomponent
plussing your component into another
component will inherit all objects
Injecting ActivitiesComponents scoped (recreated) with each activity allows us to do silly things
like injecting an activity into scoped objects:
@ScopeActivity
public class Toaster {
@Inject Activity activity;
private static void showToast() {
Toast.makeText(activity, message).show();
}}
Inject a Scoped Object@Inject
SnackbarUtil snackbarUtil;
there’s no need to pass activity into presenter and then
into the SnackBarMaker
objects can independently satisfy their own
dependencies.
Scoped If you try to inject something into an object with a
different scope, Dagger with give compilation error.
Scopes let objects “share” the same instance of anything in
the scope. ToolbarPresenters, IntentHolders etc.
Scopes cont’dScopes empower “activity singletons” that you
can share and not worry about reinstantiating
for each activity.
Dagger @ The New York Times
How we leveraged scope at The Times:
Inject activity intents into fragments 2 layers down
Create a toolbar presenter, each activity and its views get
access to the same presenter
Inject an alerthelper/snackbar util that can show alerts.
Dagger lets us decompose our activities
Dagger @ The New York TimesWe can inject something that is being provided
by a module in library project.
A/B Module within A/B Project provides
injectable A/B manager
API project owns API module etc. Main Project
creates a component using all the modules
Dagger @ The New York TimesWe can inject different implementations for
same interface spread across build variants
using a FlavorModule.
Amazon Flavor provides module with Amazon
Messaging
Google Flavor provides module with Google
Messaging
Sample Project
https://github.com/digitalbuddha/StoreDemo
Come work with me
http://developers.nytimes.com/careers