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

[Kseniia Shumelchyk] Building modular architecture apps with Dagger 2

[Kseniia Shumelchyk] Building modular architecture apps with Dagger 2

Presentation from GDG DevFest Ukraine 2016.
Learn more at: https://devfest.gdg.org.ua

Google Developers Group Lviv

September 09, 2016
Tweet

More Decks by Google Developers Group Lviv

Other Decks in Technology

Transcript

  1. Building modular architecture apps with Dagger 2 Kseniia Shumelchyk Software

    Engineer @ Softserve Organiser @ GDG Dnipro Coordinator @ WTM Ukraine
  2. #dfua IoC Principle • High level modules should not depend

    on low level modules. Both level modules should depend only on abstractions. • Abstractions should not depend on details. Details should depend on abstractions.
  3. #dfua Inversion of Control (IoC) Pattern benefits Inversion of Control

    will help you to avoid: • Strong coupling. When changes in one module affect other module. • Weakness. Changes in one part lead to unexpected errors in other part. • Inflexibility. Module is hard to separate from the application for reusing.
  4. #dfua Why Dependency Injection? • Dependencies are configured and injected

    externally, that leads that components can be reused. • We are working with abstractions (interfaces), so any implementation can be easily changed with new one, without pain and minimal changes in codebase. Objects insertion are isolated and decoupled. • Dependencies can be injected into components. We even can inject mock implementation of these dependencies . This makes testing much easier.
  5. #dfua JSR-330 (javax.inject) JSR-330 standard defines DI usage and implementation

    for Java and provides: • Set of annotations (and one interface) for injectable classes; • Standardised Dependency Injection API; • Consistency between DI instruments.
  6. #dfua What is Dagger? • Dagger 1 - originally created

    by Square; • Dagger 2 - re-created by Google, built on top of Dagger 1; • Pure Java library; • Alternative way for instantiate and manage your dependencies.
  7. #dfua Dagger 2 vs Dagger 1 • Injection problems are

    solved using code generation; • No reflection: using dependencies graph, configured at compile-time; • Fully traceable → easy debugging (concrete and clear call stack); • Performance: according to Google more that 15% increase; • Code obfuscation: method dispatch like ‘hand written’ code
  8. #dfua Other DI instruments • Spring (xml, start-time) • Guice

    (JSR-330, Runtime) • Dagger 1 (JSR-330, Runtime) • Dagger 2 (JSR-330, Compile-time) • Tiger (JSR-330, Compile-time)
  9. #dfua @Module @Module
 public class LocationModule {
 
 @Provides
 @Singleton


    LocationManager provideLocationManager() {
 GPSLocationManager manager = new GPSLocationManager();
 return manager;
 }
 }
  10. #dfua @Component @Component(modules = {AppModule.class, LocationModule.class})
 @Singleton
 public interface AppComponent

    {
 void inject(AppSplashActivity activity); void inject(AppLoginActivity activity); void inject(LocationActivity activity);
 }
  11. #dfua @Inject with constructor public class GPSLocationManager implements LocationManager {


    private LocationStore store;
 
 @Inject
 public GPSLocationManager(LocationStore store) {
 this.store = store;
 }
 }
  12. #dfua @Inject with fields public class LocationActivity extends BaseActivity {


    @Inject LocationManager locationManager;
 @Override
 public void onCreate() {
 App.getAppComponent.inject(this);
 }
 }
  13. #dfua public class AppLoginActivity extends BaseActivity {
 
 @Inject
 Lazy<AuthService>

    lazyAuthService;
 @Override
 public void onCreate() {
 App.getAppComponent.inject(this);
 }
 } Lazy @Inject
  14. #dfua public class AppLoginActivity extends BaseActivity {
 
 @Inject Provider<AuthService>

    authServiceProvider; @Override
 protected void onClick() {
 AuthService userAuth = authServiceProvider.get(); AuthService adminAuth = authServiceProvider.get();
 } } Provider @Inject - Factory
  15. #dfua @Module
 public class NetworkModule {
 @Provides @Named("cached")
 @Singleton
 OkHttpClient

    provideOkHttpClient(Cache cache) {
 OkHttpClient client = new OkHttpClient();
 client.setCache(cache);
 return client;
 }
 
 @Provides @Named("non_cached")
 @Singleton
 OkHttpClient provideOkHttpClient() {
 return new OkHttpClient();
 }
 }
 Qualified types
  16. #dfua public class AppLoginActivity extends BaseActivity {
 
 @Inject
 @Named("cached")


    OkHttpClient cachedClient; 
 @Inject
 @Named("non_cached")
 OkHttpClient client;
 } Qualified types with @inject
  17. #dfua Graph generation public class App extends Application {
 


    private static AppComponent appComponent;
 
 @Override
 public void onCreate() {
 super.onCreate();
 appComponent = ComponentInitializer.init();
 }
 
 public final static class ComponentInitializer {
 public static AppComponent init() {
 return DaggerAppComponent.create();
 }
 }
 }
  18. #dfua public final static class ComponentInitializer {
 public static AppComponent

    init() {
 return DaggerAppComponent. builder(). appModule(new AppModule(this)). build();
 }
 } Graph generation
  19. #dfua Activity/Service/etc. Application App Module Network Module Utils Module Context

    Asset Utils Network channel Network Utils Builds Provide 
 Context Provide 
 Network Injects (Fields, Constructors) Provide 
 Assets Provide 
 Net utils Component
  20. #dfua Modules suggestions • Context • System services (e.g. LocationManager)

    • REST service (e.g. Retrofit) • Database manager (e.g. Realm) • Message passing (e.g. EventBus) • Analytics tracker (e.g. Google Analytics)
  21. #dfua Model-View-Presenter DataManager - model layer, holds a reference to

    Retrofit, Database, etc. Presenter Layer - in the “middle” between View layer and Model Layer, doesn’t know anything about Activity/Fragment/View View Layer - combination of Activity/Fragment with implementation of the View interface and XML layout. Holds soft reference to Presenter.
  22. #dfua public class CreateUserContract {
 
 interface Actions {
 void

    registerUser(String email);
 } 
 
 interface View {
 void onUserRegistered(User user);
 void onErrorRegisteringUser(String error);
 }
 } View-Presenter contract
  23. #dfua public class CreateUserPresenter implements CreateUserContract.Actions {
 private final CreateUserContract.View

    view;
 
 @Inject
 public CreateUserPresenter(CreateUserContract.View view) {
 this.view = view;
 }
 
 @Override
 public void registerUser(String email) {
 }
 } Presenter implementation
  24. #dfua public class CreateUserActivity extends BaseActivity implements CreateUserContract.View {
 


    @Inject CreateUserPresenter presenter;
 
 private void onClick(View v) {
 presenter.registerUser("[email protected]");
 }
 
 @Override
 public void onUserRegistered(User user) {
 }
 
 @Override
 public void onErrorRegisteringUser() {
 }
 } View implementation
  25. #dfua @Module
 public class CreateUserModule {
 
 private final CreateUserContract.View

    view;
 
 public CreateUserModule(CreateUserContract.View view) {
 this.view = view;
 }
 
 @Provides
 public CreateUserContract.View provideView() {
 return view;
 }
 } Module
  26. #dfua @Singleton
 @Component(modules = {AppModule.class,NetworkModule.class})
 public interface AppComponent {
 


    CreateUserComponent plus(CreateUserModule module);
 
 void inject(AppLoginActivity activity);
 } AppComponent
  27. #dfua public class CreateUserActivity extends BaseActivity implements CreateUserContract.View {
 


    @Inject
 CreateUserPresenter presenter;
 
 @Override
 protected void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 buildAndInjectComponent();
 }
 
 private void buildAndInjectComponent() {
 App.getAppComponent().plus(new CreateUserModule(this)).inject(this);
 }
 } Injection
  28. #dfua Dagger pros • Performance - unlike other reflection-based frameworks

    it uses annotation processors, fast and flexible • Single module configuration - localised in a single point in our app, and we have full control over it • Separated and Isolated application logic • Portability - this makes your code used again and again
  29. #dfua • Unit test - inject one object instead of

    another; • Integration tests - easily mock any part of application; • Scopes - injection can be related to specific part of application; • Object decoupling - less connection between objects, code changes and refactoring is easier. Dagger pros
  30. #dfua Dagger cons • Strong types validation (e.g no injection

    from the base classes). • Component implementation (e.g DaggerAppComponent requires rebuilding the project to appear). • In case of any injection-related compile errors previously generated classes will disappear.
  31. #dfua AppLoginActivity.Java public class AppLoginActivity extends AppCompatActivity {
 
 @Inject


    AuthService auth;
 
 @Override
 protected void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 App.getAppComponent().inject(this);
 }
 } Workaround for strong types validation
  32. #dfua BaseActivity.Java public abstract class BaseActivity extends AppCompatActivity {
 


    @Override
 protected void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 inject(App.getAppComponent());
 }
 
 protected abstract void inject(AppComponent component);
 } Moving injection to the super type
  33. #dfua AppLoginActivity.Java public class AppLoginActivity extends BaseActivity {
 
 @Inject


    AuthService auth;
 
 @Override
 protected void inject(AppComponent component) {
 component.inject(this);
 } } Workaround for strong types validation