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

Mirosław Stanek

May 28, 2015
Tweet

More Decks by Mirosław Stanek

Other Decks in Technology

Transcript

  1. 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
  2. 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
  3. • DI requires a lot of boilerplate • Code hard

    to trace • Dependency injection frameworks are slow
  4. 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);
 }
 }
  5. 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
  6. DI example public class LoginActivity extends AppCompatActivity {
 
 @Inject


    LoginActivityPresenter presenter;
 
 @Override
 protected void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState); 
 getDependenciesGraph().inject(this);
 }
 }
  7. 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);
 }
 }
  8. 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);
 }
 }
  9. • No reflection in object instantiation • Compile-time validation and

    instantiation • Source code for inject adapters
  10. Generated source code @Singleton
 public class AnalyticsManager {
 
 private

    Application app;
 
 @Inject
 public AnalyticsManager(Application app) {
 this.app = app;
 }
 }
  11. 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;
 }
 }
  12. 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
  13. 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)
  14. 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
  15. 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);
 }
  16. Dagger 2 • 100% Proguard friendly • Generates fully traceable

    code • Easy to read generated code (as close to hand- written code as possible) • Performance
  17. 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();
 }
  18. 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);
 }
  19. @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
  20. @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
  21. @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
  22. @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
  23. @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();
 }
 }
  24. @Component @Singleton
 @Component(
 modules = {
 AppModule.class,
 GithubApiModule.class
 }
 )


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


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

    example http://fernandocejas.com/2015/04/11/tasting- dagger-2-on-android/ Implementation
  27. @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
  28. @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
  29. 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();
 }
  30. QA

  31. • 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