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

Dependency Injection with Dagger 2

Dependency Injection with Dagger 2

Overview of how to use Dagger 2 and the benefits of Dependency Injection frameworks

Sample project that goes along with this talk: https://github.com/bgogetap/Dagger2Demo

Brandon Gogetap

April 13, 2016
Tweet

More Decks by Brandon Gogetap

Other Decks in Programming

Transcript

  1. What is Dependency Injection? • Act of providing your objects

    with the dependencies they will need • “new”ing can be expensive and wasteful
  2. What is Dependency Injection? • Act of providing your objects

    with the dependencies they will need • “new”ing can be expensive and wasteful • Dependency Injection allows for easier reuse of dependencies
  3. Dependency Injection as a Pattern public final class DataRetriever {


    
 public DataRetriever(…) {
 /*. . .*/
 }
 
 public Call<Object> getWeather(String city) {
 OkHttpClient client = new OkHttpClient.Builder()
 .addInterceptor(new AuthInterceptor("api_key"))
 .build();
 Retrofit retrofit = new Retrofit.Builder()
 .baseUrl("...")
 .client(client)
 .build();
 WeatherService service = retrofit.create(WeatherService.class);
 
 return service.getWeatherForCity(city);
 }
 }
  4. Dependency Injection as a Pattern public final class DataRetriever {


    
 private final WeatherService service;
 
 public DataRetriever(WeatherService service) {
 this.service = service;
 }
 
 public Call<Object> getWeather(String city) {
 return service.getWeatherForCity(city);
 }
 }
  5. Dependency Injection as a Pattern • This is something we

    all do every day • Model/Library classes look clean
  6. Dependency Injection as a Pattern • This is something we

    all do every day • Model/Library classes look clean • What about calling classes?
  7. Dependency Injection as a Pattern final class DataPresenter {
 


    DataPresenter() {
 }
 
 void loadWeather() {
 OkHttpClient client = new OkHttpClient.Builder()
 .addInterceptor(new AuthInterceptor("api_key"))
 .build();
 Retrofit retrofit = new Retrofit.Builder()
 .baseUrl("...")
 .client(client)
 .build();
 WeatherService service = retrofit.create(WeatherService.class);
 DataRetriever retriever = new DataRetriever(service);
 retriever.getWeather("test");
 }
 }
  8. Dependency Injection as a Pattern final class DataPresenter { 


    private final DataRetriever retriever;
 
 DataPresenter() {
 OkHttpClient client = new OkHttpClient.Builder() /* … */;
 Retrofit retrofit = new Retrofit.Builder()
 .baseUrl("...")
 .client(client)
 .build();
 WeatherService service = retrofit.create(WeatherService.class);
 retriever = new DataRetriever(service);
 }
 
 void loadWeather() {
 retriever.getWeather("test");
 }
 }
  9. Dependency Injection as a Pattern • Traditional dependency injection shifts

    boilerplate code to calling classes • Leads to cluttered Presenters/Views/etc.
  10. Dependency Injection as a Pattern • Traditional dependency injection shifts

    boilerplate code to calling classes • Leads to cluttered Presenters/Views/etc. • Mocking or changing dependencies is difficult
  11. Dependency Injection as a Pattern • Traditional dependency injection shifts

    boilerplate code to calling classes • Leads to cluttered Presenters/Views/etc. • Mocking or changing dependencies is difficult • Ideally we’d want all instantiation of dependencies in centralized area
  12. Dependency Injection with a Framework • Frameworks help you do

    the work of “passing around” dependencies
  13. Dependency Injection with a Framework • Frameworks help you do

    the work of “passing around” dependencies • Reduce the amount of “new” calls in your classes
  14. Dependency Injection with a Framework • Frameworks help you do

    the work of “passing around” dependencies • Reduce the amount of “new” calls in your classes • Concentrate your dependencies in easy to find areas
  15. Dependency Injection with Dagger 2 • Dagger 2 is a

    dependency injection framework developed by Google
  16. Dependency Injection with Dagger 2 • Dagger 2 is a

    dependency injection framework developed by Google • Forked from Dagger which was created by Square (surprise!)
  17. Dependency Injection with Dagger 2 • Dagger 2 is a

    dependency injection framework developed by Google • Forked from Dagger which was created by Square (surprise!) • Group your dependency instantiation in “Modules”
  18. Dependency Injection with Dagger 2 • Dagger 2 is a

    dependency injection framework developed by Google • Forked from Dagger which was created by Square (surprise!) • Group your dependency instantiation in “Modules” • Provide scope to your dependencies using “Components”
  19. Dagger 2 - Modules @Module
 final class ApplicationModule {
 


    private final Application application;
 
 ApplicationModule(Application application) {
 this.application = application;
 }
 
 @Provides Context provideApplicationContext() {
 return application;
 }
 
 @Provides @Singleton OkHttpClient provideOkHttpClient() {
 return new OkHttpClient.Builder()
 .addInterceptor(new AuthInterceptor("apiKey"))
 .build();
 }
 
 @Provides @Singleton Retrofit provideRetrofit(OkHttpClient client) {
 return new Retrofit.Builder()
 .baseUrl("http://api.openweathermap.org/data/2.5/")
 .client(client)
 .addConverterFactory(GsonConverterFactory.create())
 .build();
 }
 }
  20. Dagger 2 - Modules @Module
 final class ApplicationModule {
 


    private final Application application;
 
 ApplicationModule(Application application) {
 this.application = application;
 }
 
 @Provides Context provideApplicationContext() {
 return application;
 }
 
 @Provides @Singleton OkHttpClient provideOkHttpClient() {
 return new OkHttpClient.Builder()
 .addInterceptor(new AuthInterceptor("apiKey"))
 .build();
 }
 
 @Provides @Singleton Retrofit provideRetrofit(OkHttpClient client) {
 return new Retrofit.Builder()
 .baseUrl("http://api.openweathermap.org/data/2.5/")
 .client(client)
 .addConverterFactory(GsonConverterFactory.create())
 .build();
 }
 }
  21. Dagger 2 - Modules @Module
 final class ApplicationModule {
 


    private final Application application;
 
 ApplicationModule(Application application) {
 this.application = application;
 }
 
 @Provides Context provideApplicationContext() {
 return application;
 }
 
 @Provides @Singleton OkHttpClient provideOkHttpClient() {
 return new OkHttpClient.Builder()
 .addInterceptor(new AuthInterceptor("apiKey"))
 .build();
 }
 
 @Provides @Singleton Retrofit provideRetrofit(OkHttpClient client) {
 return new Retrofit.Builder()
 .baseUrl("http://api.openweathermap.org/data/2.5/")
 .client(client)
 .addConverterFactory(GsonConverterFactory.create())
 .build();
 }
 }
  22. Dagger 2 - Modules @Module
 final class ApplicationModule {
 


    private final Application application;
 
 ApplicationModule(Application application) {
 this.application = application;
 }
 
 @Provides Context provideApplicationContext() {
 return application;
 }
 
 @Provides @Singleton OkHttpClient provideOkHttpClient() {
 return new OkHttpClient.Builder()
 .addInterceptor(new AuthInterceptor("apiKey"))
 .build();
 }
 
 @Provides @Singleton Retrofit provideRetrofit(OkHttpClient client) {
 return new Retrofit.Builder()
 .baseUrl("http://api.openweathermap.org/data/2.5/")
 .client(client)
 .addConverterFactory(GsonConverterFactory.create())
 .build();
 }
 }
  23. Dagger 2 - Modules @Module
 final class ApplicationModule {
 


    private final Application application;
 
 ApplicationModule(Application application) {
 this.application = application;
 }
 
 @Provides Context provideApplicationContext() {
 return application;
 }
 
 @Provides @Singleton OkHttpClient provideOkHttpClient() {
 return new OkHttpClient.Builder()
 .addInterceptor(new AuthInterceptor("apiKey"))
 .build();
 }
 
 @Provides @Singleton Retrofit provideRetrofit(OkHttpClient client) {
 return new Retrofit.Builder()
 .baseUrl("http://api.openweathermap.org/data/2.5/")
 .client(client)
 .addConverterFactory(GsonConverterFactory.create())
 .build();
 }
 }
  24. Dagger 2 - Modules @Module
 final class ApplicationModule {
 


    private final Application application;
 
 ApplicationModule(Application application) {
 this.application = application;
 }
 
 @Provides Context provideApplicationContext() {
 return application;
 }
 
 @Provides @Singleton OkHttpClient provideOkHttpClient() {
 return new OkHttpClient.Builder()
 .addInterceptor(new AuthInterceptor("apiKey"))
 .build();
 }
 
 @Provides @Singleton Retrofit provideRetrofit(OkHttpClient client) {
 return new Retrofit.Builder()
 .baseUrl("http://api.openweathermap.org/data/2.5/")
 .client(client)
 .addConverterFactory(GsonConverterFactory.create())
 .build();
 }
 }
  25. Dagger 2 - Modules @Module
 final class ApplicationModule {
 


    private final Application application;
 
 ApplicationModule(Application application) {
 this.application = application;
 }
 
 @Provides Context provideApplicationContext() {
 return application;
 }
 
 @Provides @Singleton OkHttpClient provideOkHttpClient() {
 return new OkHttpClient.Builder()
 .addInterceptor(new AuthInterceptor("apiKey"))
 .build();
 }
 
 @Provides @Singleton Retrofit provideRetrofit(OkHttpClient client) {
 return new Retrofit.Builder()
 .baseUrl("http://api.openweathermap.org/data/2.5/")
 .client(client)
 .addConverterFactory(GsonConverterFactory.create())
 .build();
 }
 }
  26. Dagger 2 - Modules @Module
 final class ApplicationModule {
 


    private final Application application;
 
 ApplicationModule(Application application) {
 this.application = application;
 }
 
 @Provides Context provideApplicationContext() {
 return application;
 }
 
 @Provides @Singleton OkHttpClient provideOkHttpClient() {
 return new OkHttpClient.Builder()
 .addInterceptor(new AuthInterceptor("apiKey"))
 .build();
 }
 
 @Provides @Singleton Retrofit provideRetrofit(OkHttpClient client) {
 return new Retrofit.Builder()
 .baseUrl("http://api.openweathermap.org/data/2.5/")
 .client(client)
 .addConverterFactory(GsonConverterFactory.create())
 .build();
 }
 }
  27. Dagger 2 - Modules @Module
 final class ApplicationModule {
 


    private final Application application;
 
 ApplicationModule(Application application) {
 this.application = application;
 }
 
 @Provides Context provideApplicationContext() {
 return application;
 }
 
 @Provides @Singleton OkHttpClient provideOkHttpClient() {
 return new OkHttpClient.Builder()
 .addInterceptor(new AuthInterceptor("apiKey"))
 .build();
 }
 
 @Provides @Singleton Retrofit provideRetrofit(OkHttpClient client) {
 return new Retrofit.Builder()
 .baseUrl("http://api.openweathermap.org/data/2.5/")
 .client(client)
 .addConverterFactory(GsonConverterFactory.create())
 .build();
 }
 }
  28. Dagger 2 - Modules @Module
 final class ApplicationModule {
 


    private final Application application;
 
 ApplicationModule(Application application) {
 this.application = application;
 }
 
 @Provides Context provideApplicationContext() {
 return application;
 }
 
 @Provides @Singleton OkHttpClient provideOkHttpClient() {
 return new OkHttpClient.Builder()
 .addInterceptor(new AuthInterceptor("apiKey"))
 .build();
 }
 
 @Provides @Singleton Retrofit provideRetrofit(OkHttpClient client) {
 return new Retrofit.Builder()
 .baseUrl("http://api.openweathermap.org/data/2.5/")
 .client(client)
 .addConverterFactory(GsonConverterFactory.create())
 .build();
 }
 }
  29. Dagger 2 - Modules • Define and instantiate the dependencies

    that will be provided • Can only contain one scope (can be mixed with unscoped dependencies)
  30. Dagger 2 - Modules • Define and instantiate the dependencies

    that will be provided • Can only contain one scope (can be mixed with unscoped dependencies) • Scoped dependencies must match scope of Component
  31. Dagger 2 - Components @Singleton @Component(modules = ApplicationModule.class)
 interface ApplicationComponent

    {
 
 MainComponent plus(MainModule mainModule);
 
 ForecastComponent plus(ForecastModule forecastModule);
 
 void inject(MyApplication application);
 
 void inject(MainActivity mainActivity);
 }
  32. Dagger 2 - Components @Singleton @Component(modules = ApplicationModule.class)
 interface ApplicationComponent

    {
 
 MainComponent plus(MainModule mainModule);
 
 ForecastComponent plus(ForecastModule forecastModule);
 
 void inject(MyApplication application);
 
 void inject(MainActivity mainActivity);
 }
  33. Dagger 2 - Components @Singleton @Component(modules = ApplicationModule.class)
 interface ApplicationComponent

    {
 
 MainComponent plus(MainModule mainModule);
 
 ForecastComponent plus(ForecastModule forecastModule);
 
 void inject(MyApplication application);
 
 void inject(MainActivity mainActivity);
 }
  34. Dagger 2 - Components @Singleton @Component(modules = ApplicationModule.class)
 interface ApplicationComponent

    {
 
 MainComponent plus(MainModule mainModule);
 
 ForecastComponent plus(ForecastModule forecastModule);
 
 void inject(MyApplication application);
 
 void inject(MainActivity mainActivity);
 }
  35. Dagger 2 - Components @Singleton @Component(modules = ApplicationModule.class)
 interface ApplicationComponent

    {
 
 MainComponent plus(MainModule mainModule);
 
 ForecastComponent plus(ForecastModule forecastModule);
 
 void inject(MyApplication application);
 
 void inject(MainActivity mainActivity);
 }
  36. Dagger 2 - Components @Singleton @Component(modules = ApplicationModule.class)
 interface ApplicationComponent

    {
 
 MainComponent plus(MainModule mainModule);
 
 ForecastComponent plus(ForecastModule forecastModule);
 
 void inject(MyApplication application);
 
 void inject(MainActivity mainActivity);
 }
  37. Dagger 2 - Components @Singleton @Component(modules = ApplicationModule.class)
 interface ApplicationComponent

    {
 
 MainComponent plus(MainModule mainModule);
 
 ForecastComponent plus(ForecastModule forecastModule);
 
 void inject(MyApplication application);
 
 void inject(MainActivity mainActivity);
 }
  38. Dagger 2 - Components • The “glue” between Modules and

    Injected classes • Allow you to manage scope
  39. Dagger 2 - Components • The “glue” between Modules and

    Injected classes • Allow you to manage scope • Build the “dependency graph” out of only the modules you need for current state
  40. Dagger 2 - Components • The “glue” between Modules and

    Injected classes • Allow you to manage scope • Build the “dependency graph” out of only the modules you need for current state • Scoped dependencies will be reused if injected by same component instance
  41. Dagger 2 - Components • The “glue” between Modules and

    Injected classes • Allow you to manage scope • Build the “dependency graph” out of only the modules you need for current state • Scoped dependencies will be reused if injected by same component instance • Creating component in Activity#onCreate == new instances after config change
  42. Dagger 2 - Constructor Injection public final class DataRetriever {


    
 private final WeatherService service;
 
 @Inject public DataRetriever(WeatherService service) {
 this.service = service;
 }
 
 public Call<Object> getWeather(String city) {
 return service.getWeatherForCity(city);
 }
 }
  43. Dagger 2 - Field Injection public final class ModalView {


    
 @Inject SharedPreferences sharedPreferences;
 
 public ModalView(Context context) { /*. . .*/
 Injector.<…>getComponent(context).inject(this);
 }
 }
  44. Dagger 2 Summary • Modules provide dependencies • Components inject

    classes • Scoped dependencies persist for life of component
  45. Dependency Injection as a Pattern public final class DataRetriever {


    
 private final WeatherService service;
 
 public DataRetriever(WeatherService service) {
 this.service = service;
 }
 
 public Call<Object> getWeather(String city) {
 return service.getWeatherForCity(city);
 }
 }
  46. Dependency Injection as a Pattern public final class DataRetriever {


    
 private final WeatherService service;
 
 @Inject public DataRetriever(WeatherService service) {
 this.service = service;
 }
 
 public Call<Object> getWeather(String city) {
 return service.getWeatherForCity(city);
 }
 }
  47. Dependency Injection as a Pattern final class DataPresenter { 


    private final DataRetriever retriever;
 
 DataPresenter() {
 OkHttpClient client = new OkHttpClient.Builder() /* … */;
 Retrofit retrofit = new Retrofit.Builder()
 .baseUrl("...")
 .client(client)
 .build();
 WeatherService service = retrofit.create(WeatherService.class);
 retriever = new DataRetriever(service);
 }
 
 void loadWeather() {
 retriever.getWeather("test");
 }
 }
  48. Dependency Injection as a Pattern final class DataPresenter {
 


    private final DataRetriever retriever;
 
 @Inject DataPresenter(DataRetriever retriever) {
 this.retriever = retriever;
 }
 
 void loadWeather() {
 retriever.getWeather("Omaha").enqueue(new WeatherCallback());
 }
 }
  49. Dagger 1 or Dagger 2? Dagger 2 • No reflection

    (~13% performance gain) • Less flexible • Almost perfect compile time checks • Runtime errors are rare and easily avoided • Easier to debug* Dagger 1 • More flexible • Object graphs aren’t typed • Potential for Runtime failures • Inject calls in super class inject base class • Not a huge deal, but a nicety
  50. Benefits of using a DI Framework • Makes maintaining code

    easier • Makes modifying the dependencies easier
  51. Benefits of using a DI Framework • Makes maintaining code

    easier • Makes modifying the dependencies easier • Makes mocking for testing possible
  52. Benefits of using a DI Framework • Makes maintaining code

    easier • Makes modifying the dependencies easier • Makes mocking for testing possible • Makes following single responsibility principle easier
  53. Single Responsibility Principle • Every class should have a specific

    purpose • Splitting up code may seem like it’s increasing complexity, but it makes maintenance easier in the future
  54. Single Responsibility Principle • Every class should have a specific

    purpose • Splitting up code may seem like it’s increasing complexity, but it makes maintenance easier in the future • Manually dealing with Bundles and re-setting up your state on config changes is a pain
  55. Single Responsibility Principle • Every class should have a specific

    purpose • Splitting up code may seem like it’s increasing complexity, but it makes maintenance easier in the future • Manually dealing with Bundles and re-setting up your state on config changes is a pain • DI Frameworks make passing dependencies around to “helper” classes easier
  56. App Organization/Architecture • Bottom line is your views (Fragments, ?

    extends View, Activities) should be dumb • Delegate the handling of “state” to something else besides your views
  57. App Organization/Architecture • Bottom line is your views (Fragments, ?

    extends View, Activities) should be dumb • Delegate the handling of “state” to something else besides your views • Tell your views what to show and have them tell you what the user did
  58. App Organization/Architecture • Bottom line is your views (Fragments, ?

    extends View, Activities) should be dumb • Delegate the handling of “state” to something else besides your views • Tell your views what to show and have them tell you what the user did • All of these combined make testing / maintaining far easier
  59. Links Dagger 2 Talks: Jake Wharton - https://www.parleys.com/tutorial/5471cdd1e4b065ebcfa1d557/ Gregory Kick

    - https://www.youtube.com/watch?v=oK_XtfXPkqw Source: GitHub repo - https://github.com/google/dagger App Architecture Dependency Injection scope management: Mortar - https://github.com/square/mortar Concrete (Dagger 1) - https://github.com/JayNewstrom/Concrete Patterns: Mosby (MVP Library) - https://github.com/sockeqwe/mosby Scoop (Library - Use Views instead of Fragments) - https://github.com/lyft/scoop Android Testing Codelab (MVP Example) - https://codelabs.developers.google.com/codelabs/android-testing/ index.html?index=..%2F..%2Findex#0