Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Dagger 2

Dagger 2

Learning Dagger 2 from a historical context and examining why things are the way they are.

Michael Yotive

October 27, 2017
Tweet

More Decks by Michael Yotive

Other Decks in Programming

Transcript

  1. The most loved/hated dependency injection framework ever The most loved/hated

    dependency injection framework ever Dagger 2 Dagger 2
  2. Agenda • Dependency Injection (DI) Review • History of DI

    on Android • Dagger 2 in a nutshell ◦ How to inject a coffee maker ◦ A more realistic Dagger 2 example • Scopes • Other Random Thoughts
  3. class StarWarsService(){ public List<Film> getFilms(){ RestAdapter restAdapter = { ...

    }; StarWarsAPI starWarsAPI = restAdapter.create(StarWarsAPI.class); return starWarsAPI.getFilms(); } } Dependency Injection
  4. class StarWarsService(){ private StarWarsAPI starWarsAPI; public StarWarsService(StarWarsAPI starWarsAPI){ this.starWarsAPI =

    starWarsAPI; } public List<Film> getFilms(){ return starWarsAPI.getFilms(); } } Dependency Injection
  5. Dependency Injection • Not best practice to pass StarWarsAPI to

    an activity/fragment via a constructor. • If fragment, it’s not really best to pass via newInstance() fragment pattern, either. • A custom setter method is an option, but the “two-step” construction is an anti-pattern.
  6. public class StarWarsFragment extends Fragment { // Dependency StarWarsAPI starWarsAPI;

    @Override public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); starWarsAPI = StarWarsAPIFactory.createAPI(); } ... } Dependency Injection
  7. public class StarWarsAPIFactory{ public static StarWarsAPI create(){ OkClient client =

    OkClientFactory.create(); RequestInterceptor requestInterceptor = RequestInterceptorFactory.create(); GsonConverter gsonConverter = GsonConverterFactory.create(); RestAdapter.LogLevel logLevel = BuildConfig.LOG_NETWORK ? RestAdapter.LogLevel.FULL : RestAdapter.LogLevel.NONE; RestAdapter adapter = new RestAdapter.Builder() .setClient(client) .setLogLevel(logLevel) .setEndpoint(BuildConfig.API_URL) .setRequestInterceptor(requestInterceptor) .setConverter(gsonConverter) .build(); return adapter.create(StarWarsAPI.class); } } Dependency Injection
  8. Roboguice • Android optimized version of Guice, Google’s dependency injection

    framework. ◦ Guice was created at Google by Bob Lee (and Jessie Wilson) • Features include both View and Class injection.
  9. Roboguice @ContentView(R.layout.activity_main) public class MainActivity extends RoboActivity { @InjectView(R.id.btn_getFilms) Button

    getFilms; @Inject Logger logger; @Inject SWAPI swapi; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setupClickListener(); } private void setupClickListener() { getFilms.setOnClickListener(new View.OnClickListener() { ... }); } }
  10. Roboguice @ContentView(R.layout.activity_main) public class MainActivity extends RoboActivity { @InjectView(R.id.btn_getFilms) Button

    getFilms; @Inject Logger logger; @Inject SWAPI swapi; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setupClickListener(); } private void setupClickListener() { getFilms.setOnClickListener(new View.OnClickListener() { ... }); } }
  11. Roboguice @ContentView(R.layout.activity_main) public class MainActivity extends RoboActivity { @InjectView(R.id.btn_getFilms) Button

    getFilms; @Inject Logger logger; @Inject SWAPI swapi; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setupClickListener(); } private void setupClickListener() { getFilms.setOnClickListener(new View.OnClickListener() { ... }); } } public class RoboActivity extends Activity implements RoboContext { protected HashMap<Key<?>,Object> scopedObjects = new HashMap<Key<?>, Object>(); ... @Override public Map<Key<?>, Object> getScopedObjectMap() { return scopedObjects; } @Override public View onCreateView(String name, Context context, AttributeSet attrs) { if (RoboActivity.shouldInjectOnCreateView(name)) return RoboActivity.injectOnCreateView(name, context, attrs); return super.onCreateView(name, context, attrs); } ... }
  12. Roboguice @ContentView(R.layout.activity_main) public class MainActivity extends RoboActivity { @InjectView(R.id.btn_getFilms) Button

    getFilms; @Inject Logger logger; @Inject SWAPI swapi; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setupClickListener(); } private void setupClickListener() { getFilms.setOnClickListener(new View.OnClickListener() { ... }); } }
  13. Roboguice @ContentView(R.layout.activity_main) public class MainActivity extends RoboActivity { @InjectView(R.id.btn_getFilms) Button

    getFilms; @Inject Logger logger; @Inject SWAPI swapi; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setupClickListener(); } private void setupClickListener() { getFilms.setOnClickListener(new View.OnClickListener() { ... }); } }
  14. Roboguice @ContentView(R.layout.activity_main) public class MainActivity extends RoboActivity { @InjectView(R.id.btn_getFilms) Button

    getFilms; @Inject Logger logger; @Inject SWAPI swapi; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setupClickListener(); } private void setupClickListener() { getFilms.setOnClickListener(new View.OnClickListener() { ... }); } }
  15. Roboguice public class RetrofitProvider implements Provider<SWAPI> { @Override public SWAPI

    get() { Retrofit retrofit = GetRetrofit(); return retrofit.create(SWAPI.class); } private Retrofit GetRetrofit() { GsonBuilder builder = new GsonBuilder(); return new Retrofit.Builder() .baseUrl(BuildConfig.SWAPI_URL) .client(getHttpClient()) .addConverterFactory(GsonConverterFactory.create(builder.create())) .build(); } public OkHttpClient getHttpClient(){ ... } HttpLoggingInterceptor getInterceptor(){ … } }
  16. Roboguice public class RetrofitProvider implements Provider<SWAPI> { @Override public SWAPI

    get() { Retrofit retrofit = GetRetrofit(); return retrofit.create(SWAPI.class); } private Retrofit GetRetrofit() { GsonBuilder builder = new GsonBuilder(); return new Retrofit.Builder() .baseUrl(BuildConfig.SWAPI_URL) .client(getHttpClient()) .addConverterFactory(GsonConverterFactory.create(builder.create())) .build(); } public OkHttpClient getHttpClient(){ ... } HttpLoggingInterceptor getInterceptor(){ … } }
  17. Roboguice public class RetrofitModule implements Module { @Override public void

    configure(Binder binder) { binder.bind(SWAPI.class).toProvider(RetrofitProvider.class); } }
  18. Roboguice public class RetrofitModule implements Module { @Override public void

    configure(Binder binder) { binder.bind(SWAPI.class).toProvider(RetrofitProvider.class); } }
  19. Roboguice public class RoboGuiceExampleApplication extends Application { @Override public void

    onCreate() { super.onCreate(); RoboGuice.getOrCreateBaseApplicationInjector(this, RoboGuice.DEFAULT_STAGE, RoboGuice.newDefaultRoboModule(this), new RetrofitModule()); } }
  20. Roboguice public class RoboGuiceExampleApplication extends Application { @Override public void

    onCreate() { super.onCreate(); RoboGuice.getOrCreateBaseApplicationInjector(this, RoboGuice.DEFAULT_STAGE, RoboGuice.newDefaultRoboModule(this), new RetrofitModule()); } }
  21. Roboguice Pros • Binding Discovery • Easy Singleton Creation •

    Android specific Injections Cons • Configuration Errors occur at runtime. • Slow initialization and injections. • Deprecated (As of August 2016)
  22. Dagger (v1) • Developed at Square by Jessie Wilson (with

    guidance from Bob Lee) • Initially targeted resource constrained environments, such as Android. • Try to solve the configuration problems of guice (fail at compile time, not runtime).
  23. Dagger (v1) @Module( injects = { MainActivity.class } ) public

    class AppModule { @Provides public Logger providesLogger(){ return new Logger(); } @Provides @Singleton public SWAPI providesSWAPI(Retrofit retrofit){ return retrofit.create(SWAPI.class); } @Provides public Retrofit providesRetrofit(OkHttpClient httpClient) { … } @Provides public OkHttpClient provideHttpClient(HttpLoggingInterceptor loggingInterceptor){ … } @Provides public HttpLoggingInterceptor provideInterceptor(){ … } }
  24. Dagger (v1) @Module( injects = { MainActivity.class } ) public

    class AppModule { @Provides public Logger providesLogger(){ return new Logger(); } @Provides @Singleton public SWAPI providesSWAPI(Retrofit retrofit){ return retrofit.create(SWAPI.class); } @Provides public Retrofit providesRetrofit(OkHttpClient httpClient) { … } @Provides public OkHttpClient provideHttpClient(HttpLoggingInterceptor loggingInterceptor){ … } @Provides public HttpLoggingInterceptor provideInterceptor(){ … } }
  25. Dagger (v1) @Module( injects = { MainActivity.class } ) public

    class AppModule { @Provides public Logger providesLogger(){ return new Logger(); } @Provides @Singleton public SWAPI providesSWAPI(Retrofit retrofit){ return retrofit.create(SWAPI.class); } @Provides public Retrofit providesRetrofit(OkHttpClient httpClient) { … } @Provides public OkHttpClient provideHttpClient(HttpLoggingInterceptor loggingInterceptor){ … } @Provides public HttpLoggingInterceptor provideInterceptor(){ … } }
  26. Dagger (v1) @Module( injects = { MainActivity.class } ) public

    class AppModule { @Provides public Logger providesLogger(){ return new Logger(); } @Provides @Singleton public SWAPI providesSWAPI(Retrofit retrofit){ return retrofit.create(SWAPI.class); } @Provides public Retrofit providesRetrofit(OkHttpClient httpClient) { … } @Provides public OkHttpClient provideHttpClient(HttpLoggingInterceptor loggingInterceptor){ … } @Provides public HttpLoggingInterceptor provideInterceptor(){ … } }
  27. Dagger (v1) @Module( injects = { MainActivity.class } ) public

    class AppModule { @Provides public Logger providesLogger(){ return new Logger(); } @Provides @Singleton public SWAPI providesSWAPI(Retrofit retrofit){ return retrofit.create(SWAPI.class); } @Provides public Retrofit providesRetrofit(OkHttpClient httpClient) { … } @Provides public OkHttpClient provideHttpClient(HttpLoggingInterceptor loggingInterceptor){ … } @Provides public HttpLoggingInterceptor provideInterceptor(){ … } }
  28. Dagger (v1) public class DaggerApplication extends Application { private ObjectGraph

    objectGraph; @Override public void onCreate() { super.onCreate(); objectGraph = ObjectGraph.create(new AppModule()); } public void inject(Object object) { objectGraph.inject(object); } public static DaggerApplication Instance(Context context){ return (DaggerApplication)context.getApplicationContext(); } }
  29. Dagger (v1) public class DaggerApplication extends Application { private ObjectGraph

    objectGraph; @Override public void onCreate() { super.onCreate(); objectGraph = ObjectGraph.create(new AppModule()); } public void inject(Object object) { objectGraph.inject(object); } public static DaggerApplication Instance(Context context){ return (DaggerApplication)context.getApplicationContext(); } }
  30. Dagger (v1) public class DaggerApplication extends Application { private ObjectGraph

    objectGraph; @Override public void onCreate() { super.onCreate(); objectGraph = ObjectGraph.create(new AppModule()); } public void inject(Object object) { objectGraph.inject(object); } public static DaggerApplication Instance(Context context){ return (DaggerApplication)context.getApplicationContext(); } }
  31. Dagger (v1) public class DaggerApplication extends Application { private ObjectGraph

    objectGraph; @Override public void onCreate() { super.onCreate(); objectGraph = ObjectGraph.create(new AppModule()); } public void inject(Object object) { objectGraph.inject(object); } public static DaggerApplication Instance(Context context){ return (DaggerApplication)context.getApplicationContext(); } }
  32. Dagger (v1) public class MainActivity extends AppCompatActivity { @Inject Logger

    logger; @Inject SWAPI swapi; Button getFilms; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); DaggerApplication.Instance(this).inject(this); setupClickListener(); } private void setupClickListener() { … } }
  33. Dagger (v1) public class MainActivity extends AppCompatActivity { @Inject Logger

    logger; @Inject SWAPI swapi; Button getFilms; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); DaggerApplication.Instance(this).inject(this); setupClickListener(); } private void setupClickListener() { … } }
  34. Dagger (v1) public class MainActivity extends AppCompatActivity { @Inject Logger

    logger; @Inject SWAPI swapi; Button getFilms; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); DaggerApplication.Instance(this).inject(this); setupClickListener(); } private void setupClickListener() { … } } public class DaggerApplication extends Application { private ObjectGraph objectGraph; @Override public void onCreate() { ... } public void inject(Object object) { objectGraph.inject(object); } public static DaggerApplication Instance(Context context){ return (DaggerApplication)context.getApplicationContext(); } }
  35. Dagger (v1) public class MainActivity extends AppCompatActivity { @Inject Logger

    logger; @Inject SWAPI swapi; Button getFilms; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); DaggerApplication.Instance(this).inject(this); setupClickListener(); } private void setupClickListener() { … } } public class DaggerApplication extends Application { private ObjectGraph objectGraph; @Override public void onCreate() { ... } public void inject(Object object) { objectGraph.inject(object); } public static DaggerApplication Instance(Context context){ return (DaggerApplication)context.getApplicationContext(); } }
  36. Dagger (v1) public class MainActivity extends AppCompatActivity { @Inject Logger

    logger; @Inject SWAPI swapi; Button getFilms; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); DaggerApplication.Instance(this).inject(this); setupClickListener(); } private void setupClickListener() { … } }
  37. Dagger (v1) Pros • Activities/Fragments don’t need to extend anything

    special. • Readable Object Graph. Cons • No Android specific helpers like RoboGuice. • Could not fully analyze object graph. • Issues with ProGuard
  38. Dagger 2 • Developed by the Java Core Libraries team

    at Google. • Improve performance through code generation. ◦ This allowed for better ProGuard support. • Strong focus on traceability - knowing exactly why a configuration error has occurred.
  39. The 3 Parts of Dagger • @Inject What object do

    we want? • @Module / @Provides How do we construct the object? • @Component Where do you want to inject the object?
  40. Dagger 2 public class CoffeeMaker { private final Heater heater;

    private final Pump pump; @Inject CoffeeMaker(Heater heater, Pump pump){ this.heater = heater; this.pump = pump; } public void brew(){ heater.on(); pump.pump(); System.out.print("[_]P ~~~ "); System.out.print("Enjoy!"); heater.off(); } }
  41. Dagger 2 @Module public class CoffeeModule { @Provides Heater providesHeater(){

    return new ElectricHeater(); } @Provides Pump providesPump(){ return new Thermosiphon(); } }
  42. Dagger 2 @Module public class CoffeeModule { @Provides Heater providesHeater(){

    return new ElectricHeater(); } @Provides Pump providesPump(){ return new Thermosiphon(); } }
  43. Dagger 2 @Module public class CoffeeModule { @Provides Heater providesHeater(){

    return new ElectricHeater(); } @Provides Pump providesPump(){ return new Thermosiphon(); } }
  44. Dagger 2 @Generated( value = "dagger.internal.codegen.ComponentProcessor", comments = "https://google.github.io/dagger" )

    public final class DaggerCoffeeComponent implements CoffeeComponent { private CoffeeModule coffeeModule; ... @Override public CoffeeMaker maker() { return new CoffeeMaker( Preconditions.checkNotNull( coffeeModule.providesHeater(), "Cannot return null from a non-@Nullable @Provides method"), Preconditions.checkNotNull( coffeeModule.providesPump(), "Cannot return null from a non-@Nullable @Provides method")); } public static final class Builder { … } }
  45. Dagger 2 @Generated( value = "dagger.internal.codegen.ComponentProcessor", comments = "https://google.github.io/dagger" )

    public final class DaggerCoffeeComponent implements CoffeeComponent { private CoffeeModule coffeeModule; ... @Override public CoffeeMaker maker() { return new CoffeeMaker( Preconditions.checkNotNull( coffeeModule.providesHeater(), "Cannot return null from a non-@Nullable @Provides method"), Preconditions.checkNotNull( coffeeModule.providesPump(), "Cannot return null from a non-@Nullable @Provides method")); } public static final class Builder { … } }
  46. Dagger 2 @Generated( value = "dagger.internal.codegen.ComponentProcessor", comments = "https://google.github.io/dagger" )

    public final class DaggerCoffeeComponent implements CoffeeComponent { private CoffeeModule coffeeModule; ... @Override public CoffeeMaker maker() { return new CoffeeMaker( Preconditions.checkNotNull( coffeeModule.providesHeater(), "Cannot return null from a non-@Nullable @Provides method"), Preconditions.checkNotNull( coffeeModule.providesPump(), "Cannot return null from a non-@Nullable @Provides method")); } public static final class Builder { … } }
  47. Dagger 2 @Generated( value = "dagger.internal.codegen.ComponentProcessor", comments = "https://google.github.io/dagger" )

    public final class DaggerCoffeeComponent implements CoffeeComponent { private CoffeeModule coffeeModule; ... @Override public CoffeeMaker maker() { return new CoffeeMaker( Preconditions.checkNotNull( coffeeModule.providesHeater(), "Cannot return null from a non-@Nullable @Provides method"), Preconditions.checkNotNull( coffeeModule.providesPump(), "Cannot return null from a non-@Nullable @Provides method")); } public static final class Builder { … } }
  48. Dagger 2 public class Program { public static void main(String[]

    args){ CoffeeComponent component = DaggerCoffeeComponent.builder() .coffeeModule(new CoffeeModule()) .build(); component.maker().brew(); } }
  49. Dagger 2 public class Program { public static void main(String[]

    args){ CoffeeComponent component = DaggerCoffeeComponent.builder() .coffeeModule(new CoffeeModule()) .build(); component.maker().brew(); } }
  50. Dagger 2 public class Program { public static void main(String[]

    args){ CoffeeComponent component = DaggerCoffeeComponent.builder() .coffeeModule(new CoffeeModule()) .build(); component.maker().brew(); } }
  51. Dagger 2 public class Program { public static void main(String[]

    args){ CoffeeComponent component = DaggerCoffeeComponent.builder() .coffeeModule(new CoffeeModule()) .build(); component.maker().brew(); } }
  52. Dagger 2 Pros • Static analysis of entire graph at

    compile time. • No reflection. • Proguard Safe. Cons • Poor Samples (Especially for Android) • Steep learning curve.
  53. Android Example public class MainActivity extends AppCompatActivity { @Inject SWAPI

    swapi; private Button getFilms; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); getFilms = (Button)findViewById(R.id.btn_getFilms); ... } private void setupClickListener() { … } }
  54. Android Example @Module public class ApplicationModule { @Provides public SWAPI

    provideSWAPI(Retrofit retrofit){ return retrofit.create(SWAPI.class); } @Provides public Retrofit providesRetrofit(OkHttpClient okHttpClient) { … } @Provides public OkHttpClient providesHttpClient(HttpLoggingInterceptor interceptor){ … } @Provides public HttpLoggingInterceptor providesInterceptor(){ … } }
  55. Android Example @Component(modules = { ApplicationModule.class }) public interface ApplicationComponent

    { void inject(MainActivity mainActivity); } @Component(modules = { CoffeeModule.class }) public interface CoffeeComponent { CoffeeMaker maker(); }
  56. Android Example @Component(modules = { ApplicationModule.class }) public interface ApplicationComponent

    { void inject(MainActivity mainActivity); } Allows property level injection for any @Inject annotation
  57. Android Example @Component(modules = { ApplicationModule.class }) public interface ApplicationComponent

    { void inject(MainActivity mainActivity); } This, too, can be any name you want.
  58. Android Example public final class DaggerApplicationComponent implements ApplicationComponent { private

    ApplicationModule applicationModule; private DaggerApplicationComponent(Builder builder) { initialize(builder); } ... @Override public void inject(MainActivity mainActivity) { injectMainActivity(mainActivity); } private MainActivity injectMainActivity(MainActivity instance) { MainActivity_MembersInjector.injectSwapi( instance, Preconditions.checkNotNull( ..., "Cannot return null from a non-@Nullable @Provides method")); return instance; } ... }
  59. Android Example public final class DaggerApplicationComponent implements ApplicationComponent { private

    ApplicationModule applicationModule; private DaggerApplicationComponent(Builder builder) { initialize(builder); } ... @Override public void inject(MainActivity mainActivity) { injectMainActivity(mainActivity); } private MainActivity injectMainActivity(MainActivity instance) { MainActivity_MembersInjector.injectSwapi( instance, Preconditions.checkNotNull( ..., "Cannot return null from a non-@Nullable @Provides method")); return instance; } ... }
  60. Android Example public final class DaggerApplicationComponent implements ApplicationComponent { private

    ApplicationModule applicationModule; private DaggerApplicationComponent(Builder builder) { initialize(builder); } ... @Override public void inject(MainActivity mainActivity) { injectMainActivity(mainActivity); } private MainActivity injectMainActivity(MainActivity instance) { MainActivity_MembersInjector.injectSwapi( instance, Preconditions.checkNotNull( ..., "Cannot return null from a non-@Nullable @Provides method")); return instance; } ... }
  61. Android Example public final class MainActivity_MembersInjector implements MembersInjector<MainActivity> { private

    final Provider<SWAPI> swapiProvider; ... @Override public void injectMembers(MainActivity instance) { injectSwapi(instance, swapiProvider.get()); } public static void injectSwapi(MainActivity instance, SWAPI swapi) { instance.swapi = swapi; } }
  62. Android Example public final class MainActivity_MembersInjector implements MembersInjector<MainActivity> { private

    final Provider<SWAPI> swapiProvider; ... @Override public void injectMembers(MainActivity instance) { injectSwapi(instance, swapiProvider.get()); } public static void injectSwapi(MainActivity instance, SWAPI swapi) { instance.swapi = swapi; } }
  63. Android Example public class Dagger2ExampleApplication extends Application { private ApplicationComponent

    applicationComponent; @Override public void onCreate() { super.onCreate(); applicationComponent = DaggerApplicationComponent.builder() .applicationModule(new ApplicationModule()) .build(); } public static Dagger2ExampleApplication getApplication(Context context){ return (Dagger2ExampleApplication)context.getApplicationContext(); } public ApplicationComponent getApplicationComponent(){ return applicationComponent; } }
  64. Android Example public class Dagger2ExampleApplication extends Application { private ApplicationComponent

    applicationComponent; @Override public void onCreate() { super.onCreate(); applicationComponent = DaggerApplicationComponent.builder() .applicationModule(new ApplicationModule()) .build(); } public static Dagger2ExampleApplication getApplication(Context context){ return (Dagger2ExampleApplication)context.getApplicationContext(); } public ApplicationComponent getApplicationComponent(){ return applicationComponent; } }
  65. Android Example public class Dagger2ExampleApplication extends Application { private ApplicationComponent

    applicationComponent; @Override public void onCreate() { super.onCreate(); applicationComponent = DaggerApplicationComponent.builder() .applicationModule(new ApplicationModule()) .build(); } public static Dagger2ExampleApplication getApplication(Context context){ return (Dagger2ExampleApplication)context.getApplicationContext(); } public ApplicationComponent getApplicationComponent(){ return applicationComponent; } }
  66. Android Example public class Dagger2ExampleApplication extends Application { private ApplicationComponent

    applicationComponent; @Override public void onCreate() { super.onCreate(); applicationComponent = DaggerApplicationComponent.builder() .applicationModule(new ApplicationModule()) .build(); } public static Dagger2ExampleApplication getApplication(Context context){ return (Dagger2ExampleApplication)context.getApplicationContext(); } public ApplicationComponent getApplicationComponent(){ return applicationComponent; } }
  67. Android Example public class MainActivity extends AppCompatActivity { @Inject SWAPI

    swapi; private Button getFilms; @Override protected void onCreate(Bundle savedInstanceState) { … Dagger2ExampleApplication .getApplication(this) .getApplicationComponent() .inject(this); setupClickListener(); } private void setupClickListener() { … } }
  68. Android Example public class MainActivity extends AppCompatActivity { @Inject SWAPI

    swapi; private Button getFilms; @Override protected void onCreate(Bundle savedInstanceState) { … Dagger2ExampleApplication .getApplication(this) .getApplicationComponent() .inject(this); setupClickListener(); } private void setupClickListener() { … } }
  69. Scopes • Mechanism for creating single instances of classes around

    as long as the component exists. • Think about “scopes” for functions or methods.
  70. Scopes • Dagger comes with one built-in scope: @Singleton ◦

    I would advise not using this, though. • Custom scopes aren’t hard to make and create clarity for the life of the instance.
  71. Scopes @Module public class ApplicationModule { @Provides @ApplicationScope public SWAPI

    provideSWAPI(Retrofit retrofit){ return retrofit.create(SWAPI.class); } ... }
  72. Scopes • Custom Scopes allow us to have granular control

    over singleton creation. • You can only have one Custom Scope per component. • We could create application scoped instances, but it would be more performant to only create/keep objects when you need them. (think “local” singletons)
  73. Scopes ApplicationComponent (@ApplicationScope) ActivityComponent (@ActivityScope) ActivityComponent (@ActivityScope) ActivityComponent (@ActivityScope) Fragment/

    Presenter Fragment/ Presenter Fragment/ Presenter Application Lifecycle How do we share the entire object graph?
  74. Component Dependency • Child components have access to parent component’s

    object graph. • Allows you to keep components independant (separate scopes). • Explicitly show how dependencies are used across components.
  75. Component Dependency @ActivityScope @Component( dependencies = { ApplicationComponent.class }, modules

    = { ActivityModule.class } ) public interface ActivityComponent { void inject(MainActivity mainActivity); }
  76. Component Dependency @ActivityScope @Component( dependencies = { ApplicationComponent.class }, modules

    = { ActivityModule.class } ) public interface ActivityComponent { void inject(MainActivity mainActivity); }
  77. Component Dependency public class MainActivity extends AppCompatActivity { ... private

    ActivityComponent activityComponent; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ... ApplicationComponent applicationComponent = Dagger2ExampleApplication.getApplication(this) .getApplicationComponent(); activityComponent = DaggerActivityComponent.builder() .applicationComponent(applicationComponent) .build(); activityComponent.inject(this); } ... }
  78. Component Dependency public class MainActivity extends AppCompatActivity { ... private

    ActivityComponent activityComponent; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ... ApplicationComponent applicationComponent = Dagger2ExampleApplication.getApplication(this) .getApplicationComponent(); activityComponent = DaggerActivityComponent.builder() .applicationComponent(applicationComponent) .build(); activityComponent.inject(this); } ... }
  79. Component Dependency public class MainActivity extends AppCompatActivity { ... private

    ActivityComponent activityComponent; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ... ApplicationComponent applicationComponent = Dagger2ExampleApplication.getApplication(this) .getApplicationComponent(); activityComponent = DaggerActivityComponent.builder() .applicationComponent(applicationComponent) .build(); activityComponent.inject(this); } ... }
  80. Component Dependency public class MainActivity extends AppCompatActivity { ... private

    ActivityComponent activityComponent; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ... ApplicationComponent applicationComponent = Dagger2ExampleApplication.getApplication(this) .getApplicationComponent(); activityComponent = DaggerActivityComponent.builder() .applicationComponent(applicationComponent) .build(); activityComponent.inject(this); } ... }
  81. Component Dependency public class MainActivity extends AppCompatActivity { ... private

    ActivityComponent activityComponent; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ... ApplicationComponent applicationComponent = Dagger2ExampleApplication.getApplication(this) .getApplicationComponent(); activityComponent = DaggerActivityComponent.builder() .applicationComponent(applicationComponent) .build(); activityComponent.inject(this); } ... }
  82. Subcomponents • Another way to have a relationship between components

    and keep scopes separate. • Child component is added to parent component. • Because you are adding the child component to the parent, you must manage the life of the child component.
  83. Subcomponent @ApplicationScope @Component(modules = { ApplicationModule.class }) public interface ApplicationComponent

    { void inject(MainActivity mainActivity); ActivityComponent plus(ActivityModule activityModule); }
  84. Subcomponent public class Dagger2ExampleApplication extends Application { private ApplicationComponent applicationComponent;

    private ActivityComponent activityComponent; @Override public void onCreate() { super.onCreate(); applicationComponent = DaggerApplicationComponent.builder() .applicationModule(new ApplicationModule()) .build(); } … public ActivityComponent createActivityComponent() { activityComponent = applicationComponent.plus(new ActivityModule()); return activityComponent; } public void releaseActivityComponent() { activityComponent = null; } }
  85. Subcomponent public class Dagger2ExampleApplication extends Application { private ApplicationComponent applicationComponent;

    private ActivityComponent activityComponent; @Override public void onCreate() { super.onCreate(); applicationComponent = DaggerApplicationComponent.builder() .applicationModule(new ApplicationModule()) .build(); } … public ActivityComponent createActivityComponent() { activityComponent = applicationComponent.plus(new ActivityModule()); return activityComponent; } public void releaseActivityComponent() { activityComponent = null; } }
  86. Subcomponent public class Dagger2ExampleApplication extends Application { private ApplicationComponent applicationComponent;

    private ActivityComponent activityComponent; @Override public void onCreate() { super.onCreate(); applicationComponent = DaggerApplicationComponent.builder() .applicationModule(new ApplicationModule()) .build(); } … public ActivityComponent createActivityComponent() { activityComponent = applicationComponent.plus(new ActivityModule()); return activityComponent; } public void releaseActivityComponent() { activityComponent = null; } }
  87. Subcomponent public class Dagger2ExampleApplication extends Application { private ApplicationComponent applicationComponent;

    private ActivityComponent activityComponent; @Override public void onCreate() { super.onCreate(); applicationComponent = DaggerApplicationComponent.builder() .applicationModule(new ApplicationModule()) .build(); } … public ActivityComponent createActivityComponent() { activityComponent = applicationComponent.plus(new ActivityModule()); return activityComponent; } public void releaseActivityComponent() { activityComponent = null; } }
  88. Miscellaneous • More Lessons on Dagger 2 on Caster.io https://caster.io/courses/dagger2/

    • Specifically, for testing, checkout “Testing with Dagger 2 and Mockito” https://caster.io/episodes/dagger-2-part-5/
  89. Conclusion • Dependency Injection allows us keep our code clean,

    modular, and testable. • The hardest part of Dagger 2 is understanding what is happening under the covers. • Dagger 2, for all of it’s quirks, is still one of the most performant DI frameworks.