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

Dependency Injection with Dagger 2

Dependency Injection with Dagger 2

Presentation I gave at Google I/O Extended 2015 in @techspacekrk - www.meetup.com/GDG-Krakow/events/221822600/

Source code is available: https://github.com/frogermcs/GithubClient

Bce40bbee247856d407f1d732fda01c0?s=128

Mirosław Stanek

May 28, 2015
Tweet

Transcript

  1. Dependency Injection with Dagger 2 Miroslaw Stanek

  2. about.me/froger_mcs @froger_mcs Head of mobile @ Azimo

  3. Why should I use DI?

  4. None
  5. Without DI class UserManager {
 private ApiService apiService;
 private UserStore

    userStore;
 
 public UserManager() {
 this.apiService = new ApiSerivce();
 this.userStore = new UserStore();
 }
 
 void registerUser() {/* */}
 }
 
 class RegisterActivity extends Activity {
 
 private UserManager userManager;
 
 @Override
 protected void onCreate(Bundle b) {
 this.userManager = new UserManager();
 }
 
 public void onRegisterClick(View v) {
 userManager.registerUser();
 }
 } class UserManager {
 private ApiService apiService;
 private UserStore userStore;
 
 public UserManager(ApiService apiService,
 UserStore userStore) {
 this.apiService = apiService;
 this.userStore = userStore;
 }
 
 void registerUser() {/* */}
 }
 
 class RegisterActivity extends Activity {
 
 private UserManager userManager;
 
 @Override
 protected void onCreate(Bundle b) {
 ApiService api = ApiService.getInstance();
 UserStore store = UserStore.getInstance();
 this.userManager = 
 new UserManager(api, store);
 }
 
 public void onRegisterClick(View v) {
 userManager.registerUser();
 }
 } With DI
  6. Without DI class UserManager {
 private ApiService apiService;
 private UserStore

    userStore;
 
 public UserManager() {
 this.apiService = new ApiSerivce();
 this.userStore = new UserStore();
 }
 
 void registerUser() {/* */}
 }
 
 class RegisterActivity extends Activity {
 
 private UserManager userManager;
 
 @Override
 protected void onCreate(Bundle b) {
 this.userManager = new UserManager();
 }
 
 public void onRegisterClick(View v) {
 userManager.registerUser();
 }
 } class UserManager {
 private ApiService apiService;
 private UserStore userStore;
 
 public UserManager(ApiService apiService,
 UserStore userStore) {
 this.apiService = apiService;
 this.userStore = userStore;
 }
 
 void registerUser() {/* */}
 }
 
 class RegisterActivity extends Activity {
 
 private UserManager userManager;
 
 @Override
 protected void onCreate(Bundle b) {
 ApiService api = ApiService.getInstance();
 UserStore store = UserStore.getInstance();
 this.userManager = 
 new UserManager(api, store);
 }
 
 public void onRegisterClick(View v) {
 userManager.registerUser();
 }
 } With DI
  7. Without DI With DI

  8. Why DI is bad?

  9. • DI requires a lot of boilerplate • Code hard

    to trace • Dependency injection frameworks are slow
  10. Dependency Injection frameworks

  11. DI example LoginActivity LoginActivityPresenter UserManager ApiService SharedPreferences UserDataStore

  12. DI example (without DI framework) public class LoginActivity extends AppCompatActivity

    {
 
 LoginActivityPresenter presenter;
 
 @Override
 protected void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 
 OkHttpClient okHttpClient = new OkHttpClient();
 RestAdapter.Builder builder = new RestAdapter.Builder();
 builder.setClient(new OkClient(okHttpClient))
 RestAdapter restAdapter = builder.build();
 
 ApiService apiService = restAdapter.create(ApiService.class);
 UserManager userManager = UserManager.getInstance(apiService);
 UserDataStore userDataStore = UserDataStore.getInstance(
 getSharedPreferences("prefs", MODE_PRIVATE)
 );
 
 presenter = new LoginActivityPresenter(this, userManager, userDataStore);
 }
 }
  13. DI example LoginActivity LoginActivityPresenter UserManager ApiService SharedPreferences UserDataStore RegisterActivity RegisterActivityPresenter

    UserManager ApiService SharedPreferences UserDataStore
  14. DI example (without Dagger) public class LoginActivity extends AppCompatActivity {


    
 LoginActivityPresenter presenter;
 
 @Override
 protected void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 
 OkHttpClient okHttpClient = new OkHttpClient();
 RestAdapter.Builder builder = new RestAdapter.Builder();
 builder.setClient(new OkClient(okHttpClient))
 RestAdapter restAdapter = builder.build();
 
 ApiService apiService = restAdapter.create(ApiService.class);
 UserManager userManager = UserManager.getInstance(apiService);
 UserDataStore userDataStore = public class RegisterActivity extends AppCompatActivity {
 
 RegisterActivityPresenter presenter;
 
 @Override
 protected void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 
 OkHttpClient okHttpClient = new OkHttpClient();
 RestAdapter.Builder builder = new RestAdapter.Builder();
 builder.setClient(new OkClient(okHttpClient))
 RestAdapter restAdapter = builder.build();
 
 ApiService apiService = restAdapter.create(ApiService.class);
 UserManager userManager = UserManager.getInstance(apiService);
 UserDataStore userDataStore = UserDataStore.getInstance(
 getSharedPreferences("prefs", MODE_PRIVATE)
 );
 
 presenter = new RegisterActivityPresenter(this, userManager, userDataStore);
 }
 } 2x
  15. DI example public class LoginActivity extends AppCompatActivity {
 
 @Inject


    LoginActivityPresenter presenter;
 
 @Override
 protected void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState); 
 getDependenciesGraph().inject(this);
 }
 }
  16. DI example (with Dagger 2) public class LoginActivity extends AppCompatActivity

    {
 
 @Inject
 LoginActivityPresenter presenter;
 
 @Override
 protected void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState); 
 DaggerSplashActivityComponent.builder()
 .appComponent(getAppComponent())
 .splashActivityModule(new SplashActivityModule(this))
 .build()
 .inject(this);
 }
 }
  17. DI example (with Dagger 2) public class LoginActivity extends AppCompatActivity

    {
 
 @Inject
 LoginActivityPresenter presenter;
 
 @Override
 protected void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState); 
 DaggerSplashActivityComponent.builder()
 .appComponent(getAppComponent())
 .splashActivityModule(new SplashActivityModule(this))
 .build()
 .inject(this);
 }
 }
  18. No more instantiation.

  19. DI framework cons (historical perspective)

  20. • Performance (reflection usage) • Runtime graph validation • Instantiation

    magic (no source code)
  21. Dagger 1 All problems gone!

  22. • No reflection in object instantiation • Compile-time validation and

    instantiation • Source code for inject adapters
  23. Almost all problems gone…

  24. Dagger 1 flaws

  25. Generated source code @Singleton
 public class AnalyticsManager {
 
 private

    Application app;
 
 @Inject
 public AnalyticsManager(Application app) {
 this.app = app;
 }
 }
  26. Generated source code public final class AnalyticsManager$$InjectAdapter extends Binding<AnalyticsManager>
 implements

    Provider<AnalyticsManager> {
 private Binding<android.app.Application> app;
 
 public AnalyticsManager$$InjectAdapter() {
 super("frogermcs.io.githubclient.utils.AnalyticsManager", "members/ frogermcs.io.githubclient.utils.AnalyticsManager", IS_SINGLETON, AnalyticsManager.class);
 }
 
 @Override
 @SuppressWarnings("unchecked")
 public void attach(Linker linker) {
 app = (Binding<android.app.Application>) linker.requestBinding("android.app.Application", AnalyticsManager.class, getClass().getClassLoader());
 }
 
 @Override
 public void getDependencies(Set<Binding<?>> getBindings, Set<Binding<?>> injectMembersBindings) {
 getBindings.add(app);
 }
 
 @Override
 public AnalyticsManager get() {
 AnalyticsManager result = new AnalyticsManager(app.get());
 return result;
 }
 }
  27. Proguard -dontwarn dagger.internal.codegen.** 
 -keepclassmembers,allowobfuscation class * {
 @javax.inject.* *;


    @dagger.* *;
 <init>();
 } 
 -keep class dagger.* { *; }
 -keep class javax.inject.* { *; }
 -keep class * extends dagger.internal.Binding
 -keep class * extends dagger.internal.ModuleAdapter
 -keep class * extends dagger.internal.StaticInjection
  28. Proguard Process: frogermcs.io.githubclient, PID: 32685 java.lang.RuntimeException: Unable to create application

    frogermcs.io.githubclient.App: java.lang.IllegalStateException: Module adapter for class frogermcs.io.githubclient.app.b could not be loaded. Please ensure that code generation was run for this module. at android.app.ActivityThread.handleBindApplication(ActivityThread.java:4556) … at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:698) Caused by: java.lang.IllegalStateException: Module adapter for class frogermcs.io.githubclient.b could not be loaded. Please ensure that code generation was run for this module. at dagger.internal.h.a(Unknown Source) at dagger.internal.h.a(Unknown Source) at dagger.internal.r.b(Unknown Source) at dagger.internal.g.a(Unknown Source) at dagger.internal.t.a(Unknown Source)
  29. Reflection, Runtime graph composition • „Reflection is used to figure

    out how everything fits together”
 
 (DAGGER 2 - A New Type of dependency injection - https://www.youtube.com/watch?v=oK_XtfXPkqw ) • Runtime graph composition
  30. Dagger 1 API public abstract class ObjectGraph {
 public static

    ObjectGraph create(Object... modules);
 public abstract ObjectGraph plus(Object... modules);
 public abstract <T> T get(Class<T> type);
 public abstract <T> T inject(T instance);
 public abstract void validate();
 public abstract void injectStatics();
 }
 
 public @interface Module {
 Class<?>[] injects() default { };
 Class<?>[] staticInjections() default { };
 Class<?>[] includes() default { };
 Class<?> addsTo() default Void.class;
 boolean overrides() default false;
 boolean complete() default true;
 boolean library() default false;
 }
 
 public @interface Provides {
 }
 
 public interface Lazy<T> {
 T get();
 }
 
 public interface MembersInjector<T> {
 void injectMembers(T instance);
 }
  31. Dagger 2 Silver bullet?

  32. Dagger 2 • 100% Proguard friendly • Generates fully traceable

    code • Easy to read generated code (as close to hand- written code as possible) • Performance
  33. Dagger 2 API public @interface Component {
 Class<?>[] modules() default

    {};
 Class<?>[] dependencies() default {};
 }
 
 public @interface Subcomponent {
 Class<?>[] modules() default {};
 }
 
 public @interface Module {
 Class<?>[] includes() default {};
 }
 
 public @interface Provides {
 }
 
 public @interface MapKey {
 boolean unwrapValue() default true;
 }
 
 public interface Lazy<T> {
 T get();
 }
  34. Dagger 1 API public abstract class ObjectGraph {
 public static

    ObjectGraph create(Object... modules);
 public abstract ObjectGraph plus(Object... modules);
 public abstract <T> T get(Class<T> type);
 public abstract <T> T inject(T instance);
 public abstract void validate();
 public abstract void injectStatics();
 }
 
 public @interface Module {
 Class<?>[] injects() default { };
 Class<?>[] staticInjections() default { };
 Class<?>[] includes() default { };
 Class<?> addsTo() default Void.class;
 boolean overrides() default false;
 boolean complete() default true;
 boolean library() default false;
 }
 
 public @interface Provides {
 }
 
 public interface Lazy<T> {
 T get();
 }
 
 public interface MembersInjector<T> {
 void injectMembers(T instance);
 }
  35. DI with Dagger 2 fundamentals

  36. @Inject JSR-330

  37. @Inject (constructor injection) public class LoginActivityPresenter {
 private LoginActivity loginActivity;


    private UserDataStore userDataStore;
 private UserManager userManager;
 
 @Inject
 public LoginActivityPresenter(LoginActivity loginActivity, 
 UserDataStore userDataStore, 
 UserManager userManager) {
 this.loginActivity = loginActivity;
 this.userDataStore = userDataStore;
 this.userManager = userManager;
 }
 } JSR-330
  38. @Inject (fields injection) public class LoginActivity extends AppCompatActivity {
 


    @Inject
 LoginActivityPresenter presenter;
 @Inject
 AnalyticsManager analyticsManager; 
 @Override
 protected void onCreate(Bundle bundle) {
 super.onCreate(bundle);
 getAppComponent().inject(this);
 }
 } JSR-330
  39. @Inject (method injection) public class LoginActivityPresenter {
 private LoginActivity loginActivity;


    
 @Inject
 public LoginActivityPresenter(LoginActivity loginActivity) {
 this.loginActivity = loginActivity;
 this.userDataStore = userDataStore;
 } @Inject
 public void enableWatches(Watches watches) {
 watches.register(this);
 }
 } JSR-330
  40. @Module @Module
 public class GithubApiModule {
 
 @Provides
 @Singleton
 OkHttpClient

    provideOkHttpClient() {
 OkHttpClient okHttpClient = new OkHttpClient();
 okHttpClient.setConnectTimeout(60 * 1000, TimeUnit.MILLISECONDS);
 okHttpClient.setReadTimeout(60 * 1000, TimeUnit.MILLISECONDS);
 return okHttpClient;
 }
 
 @Provides
 @Singleton
 RestAdapter provideRestAdapter(Application application, OkHttpClient okHttpClient) {
 RestAdapter.Builder builder = new RestAdapter.Builder();
 builder.setClient(new OkClient(okHttpClient))
 .setEndpoint(application.getString(R.string.endpoint));
 return builder.build();
 }
 } Dagger 2
  41. @Provide Dagger 2 @Module
 public class GithubApiModule {
 
 @Provides


    @Singleton
 OkHttpClient provideOkHttpClient() {
 OkHttpClient okHttpClient = new OkHttpClient();
 okHttpClient.setConnectTimeout(60 * 1000, TimeUnit.MILLISECONDS);
 okHttpClient.setReadTimeout(60 * 1000, TimeUnit.MILLISECONDS);
 return okHttpClient;
 }
 
 @Provides
 @Singleton
 RestAdapter provideRestAdapter(Application application, OkHttpClient okHttpClient) {
 RestAdapter.Builder builder = new RestAdapter.Builder();
 builder.setClient(new OkClient(okHttpClient))
 .setEndpoint(application.getString(R.string.endpoint));
 return builder.build();
 }
 }
  42. @Component @Singleton
 @Component(
 modules = {
 AppModule.class,
 GithubApiModule.class
 }
 )


    public interface AppComponent {
 void inject(GithubClientApplication githubClientApplication);
 
 Application getApplication();
 
 AnalyticsManager getAnalyticsManager();
 
 UserManager getUserManager();
 
 } Dagger 2
  43. @Component @ActivityScope
 @Component(
 modules = SplashActivityModule.class,
 dependencies = AppComponent.class
 )


    public interface SplashActivityComponent {
 SplashActivity inject(SplashActivity splashActivity);
 
 SplashActivityPresenter presenter();
 } Dagger 2
  44. @Scope @Scope
 public @interface PerActivity { 
 } JSR-330 Scoping

    example http://fernandocejas.com/2015/04/11/tasting- dagger-2-on-android/ Implementation
  45. @MapKey @MapKey(unwrapValue = true)
 @interface TestKey {
 String value();
 }

    @Provides(type = Type.MAP)
 @TestKey("foo")
 String provideFooKey() {
 return "foo value";
 }
 
 @Provides(type = Type.MAP)
 @TestKey("bar")
 String provideBarKey() {
 return "bar value";
 } @Inject
 Map<String, String> map; map.toString() => „{foo=foo value, bar=bar value}” Dagger 2 Definition Usage
  46. @Qualifier @Provides
 @Singleton
 @GithubRestAdapter //Qualifier
 RestAdapter provideRestAdapter() {
 return new

    RestAdapter.Builder()
 .setEndpoint("https://api.github.com")
 .build();
 }
 
 @Provides
 @Singleton
 @FacebookRestAdapter //Qualifier
 RestAdapter provideRestAdapter() {
 return new RestAdapter.Builder()
 .setEndpoint("https://api.facebook.com")
 .build();
 } JSR-330
  47. @Qualifier @Inject
 @GithubRestAdapter
 RestAdapter githubRestAdapter;
 
 @Inject
 @FacebookRestAdapter
 RestAdapter facebookRestAdapter;

    JSR-330
  48. Dagger 2 API public @interface Component {
 Class<?>[] modules() default

    {};
 Class<?>[] dependencies() default {};
 }
 
 public @interface Subcomponent {
 Class<?>[] modules() default {};
 }
 
 public @interface Module {
 Class<?>[] includes() default {};
 }
 
 public @interface Provides {
 }
 
 public @interface MapKey {
 boolean unwrapValue() default true;
 }
 
 public interface Lazy<T> {
 T get();
 }
  49. App example

  50. None
  51. None
  52. None
  53. Source code https://github.com/frogermcs/GithubClient

  54. Problems & doubts

  55. • Scoping • Testing 
 https://github.com/google/dagger/issues/110 • Dagger 1 vs

    Dagger 2
  56. QA

  57. • GithubClient source code
 https://github.com/frogermcs/GithubClient • DAGGER 2 - A

    New Type of dependency injection
 https://www.youtube.com/watch?v=oK_XtfXPkqw • Dagger 1 to 2 migration process
 http://frogermcs.github.io/dagger-1-to-2-migration/ • The Future of Dependency Injection with Dagger 2
 https://www.parleys.com/tutorial/the-future-dependency-injection- dagger-2
  58. mirek@azimo.com