Slide 1

Slide 1 text

Dependency Injection with Dagger 2 Miroslaw Stanek

Slide 2

Slide 2 text

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

Slide 3

Slide 3 text

Why should I use DI?

Slide 4

Slide 4 text

No content

Slide 5

Slide 5 text

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

Slide 6

Slide 6 text

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

Slide 7

Slide 7 text

Without DI With DI

Slide 8

Slide 8 text

Why DI is bad?

Slide 9

Slide 9 text

• DI requires a lot of boilerplate • Code hard to trace • Dependency injection frameworks are slow

Slide 10

Slide 10 text

Dependency Injection frameworks

Slide 11

Slide 11 text

DI example LoginActivity LoginActivityPresenter UserManager ApiService SharedPreferences UserDataStore

Slide 12

Slide 12 text

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);
 }
 }

Slide 13

Slide 13 text

DI example LoginActivity LoginActivityPresenter UserManager ApiService SharedPreferences UserDataStore RegisterActivity RegisterActivityPresenter UserManager ApiService SharedPreferences UserDataStore

Slide 14

Slide 14 text

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

Slide 15

Slide 15 text

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

Slide 16

Slide 16 text

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);
 }
 }

Slide 17

Slide 17 text

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);
 }
 }

Slide 18

Slide 18 text

No more instantiation.

Slide 19

Slide 19 text

DI framework cons (historical perspective)

Slide 20

Slide 20 text

• Performance (reflection usage) • Runtime graph validation • Instantiation magic (no source code)

Slide 21

Slide 21 text

Dagger 1 All problems gone!

Slide 22

Slide 22 text

• No reflection in object instantiation • Compile-time validation and instantiation • Source code for inject adapters

Slide 23

Slide 23 text

Almost all problems gone…

Slide 24

Slide 24 text

Dagger 1 flaws

Slide 25

Slide 25 text

Generated source code @Singleton
 public class AnalyticsManager {
 
 private Application app;
 
 @Inject
 public AnalyticsManager(Application app) {
 this.app = app;
 }
 }

Slide 26

Slide 26 text

Generated source code public final class AnalyticsManager$$InjectAdapter extends Binding
 implements Provider {
 private Binding 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) linker.requestBinding("android.app.Application", AnalyticsManager.class, getClass().getClassLoader());
 }
 
 @Override
 public void getDependencies(Set> getBindings, Set> injectMembersBindings) {
 getBindings.add(app);
 }
 
 @Override
 public AnalyticsManager get() {
 AnalyticsManager result = new AnalyticsManager(app.get());
 return result;
 }
 }

Slide 27

Slide 27 text

Proguard -dontwarn dagger.internal.codegen.** 
 -keepclassmembers,allowobfuscation class * {
 @javax.inject.* *;
 @dagger.* *;
 ();
 } 
 -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

Slide 28

Slide 28 text

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)

Slide 29

Slide 29 text

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

Slide 30

Slide 30 text

Dagger 1 API public abstract class ObjectGraph {
 public static ObjectGraph create(Object... modules);
 public abstract ObjectGraph plus(Object... modules);
 public abstract T get(Class type);
 public abstract 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 get();
 }
 
 public interface MembersInjector {
 void injectMembers(T instance);
 }

Slide 31

Slide 31 text

Dagger 2 Silver bullet?

Slide 32

Slide 32 text

Dagger 2 • 100% Proguard friendly • Generates fully traceable code • Easy to read generated code (as close to hand- written code as possible) • Performance

Slide 33

Slide 33 text

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 get();
 }

Slide 34

Slide 34 text

Dagger 1 API public abstract class ObjectGraph {
 public static ObjectGraph create(Object... modules);
 public abstract ObjectGraph plus(Object... modules);
 public abstract T get(Class type);
 public abstract 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 get();
 }
 
 public interface MembersInjector {
 void injectMembers(T instance);
 }

Slide 35

Slide 35 text

DI with Dagger 2 fundamentals

Slide 36

Slide 36 text

@Inject JSR-330

Slide 37

Slide 37 text

@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

Slide 38

Slide 38 text

@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

Slide 39

Slide 39 text

@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

Slide 40

Slide 40 text

@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

Slide 41

Slide 41 text

@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();
 }
 }

Slide 42

Slide 42 text

@Component @Singleton
 @Component(
 modules = {
 AppModule.class,
 GithubApiModule.class
 }
 )
 public interface AppComponent {
 void inject(GithubClientApplication githubClientApplication);
 
 Application getApplication();
 
 AnalyticsManager getAnalyticsManager();
 
 UserManager getUserManager();
 
 } Dagger 2

Slide 43

Slide 43 text

@Component @ActivityScope
 @Component(
 modules = SplashActivityModule.class,
 dependencies = AppComponent.class
 )
 public interface SplashActivityComponent {
 SplashActivity inject(SplashActivity splashActivity);
 
 SplashActivityPresenter presenter();
 } Dagger 2

Slide 44

Slide 44 text

@Scope @Scope
 public @interface PerActivity { 
 } JSR-330 Scoping example http://fernandocejas.com/2015/04/11/tasting- dagger-2-on-android/ Implementation

Slide 45

Slide 45 text

@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 map; map.toString() => „{foo=foo value, bar=bar value}” Dagger 2 Definition Usage

Slide 46

Slide 46 text

@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

Slide 47

Slide 47 text

@Qualifier @Inject
 @GithubRestAdapter
 RestAdapter githubRestAdapter;
 
 @Inject
 @FacebookRestAdapter
 RestAdapter facebookRestAdapter; JSR-330

Slide 48

Slide 48 text

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 get();
 }

Slide 49

Slide 49 text

App example

Slide 50

Slide 50 text

No content

Slide 51

Slide 51 text

No content

Slide 52

Slide 52 text

No content

Slide 53

Slide 53 text

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

Slide 54

Slide 54 text

Problems & doubts

Slide 55

Slide 55 text

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

Slide 56

Slide 56 text

QA

Slide 57

Slide 57 text

• 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

Slide 58

Slide 58 text

mirek@azimo.com