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

A Tale of Two Daggers (Square SF 2016)

A Tale of Two Daggers (Square SF 2016)

For the past three years, Square Register for Android has leveraged Dagger to wire up Java objects. However, Register’s scope hierarchy and complexity are increasing and pushing the limits on Dagger.

This lightning talk highlights how and why the Register team incrementally migrated the Android app to Dagger 2.

Presented at https://www.showclix.com/event/square-presents-feb

Video: https://www.youtube.com/watch?v=3B7F7emCc64

John Rodriguez

February 18, 2016
Tweet

More Decks by John Rodriguez

Other Decks in Programming

Transcript

  1. Scope Hierarchy Register App Scope User Logged Out User Logged

    In RootActivity Items Applet Reports Applet
  2. Scope Hierarchy User Logged In Scope Register App Scope Root

    Activity Scope Seller Flow Home Screen
  3. Scope Hierarchy User Logged In Scope Register App Scope Root

    Activity Scope Seller Flow Home Screen
  4. Scope Hierarchy User Logged In Scope Register App Scope Root

    Activity Scope Seller Flow Home Screen Tender Path Payment Type Screen
  5. Module Provider Methods @Module(
 injects = {
 GiftCardBalanceDetailsView.class,
 …
 })


    public class GiftCardBalanceDetailsModule {
 @Provides @Singleton
 GiftCardBalanceDetailsPresenter provideGiftCardBalanceDetailsPresenter(…) {
 return new GiftCardBalanceDetailsPresenter(…);
 }
 }
  6. Dagger 2: Custom Scopes import javax.inject.Scope;
 
 @Scope public @interface

    CustomScope {…} @Scope public @interface CustomScope2 {…} @Scope public @interface CustomScope3 {…} ...
  7. Dagger 2: Custom Scopes @Scope public @interface SingleIn {
 Class<?>

    value();
 } @SingleIn(SignScreen.class) class SignPresenter {…} @SingleIn(SignScreen.class) @Subcomponent(…) public interface SignScreenComponent {…}
  8. How: Migration Path • 226 screens with Dagger modules •

    35 override modules • 960 scoped instances • Full migration all at once? Not happening!
  9. But first… • solution: rename annotations using Maven shade plugin

    • @Inject (Dagger) vs @Inject2 (Dagger 2) • Dagger and Dagger 2 rely mostly on the same annotations
  10. Bottom up migration: before @WithModule(SignScreenModule.class)
 class SignScreen extends RegisterPath {


    @Module(
 addsTo = BuyerFlowModule.class,
 injects = SignView.class)
 public static class SignScreenModule {
 }
 
 @Singleton static class Presenter {
 @Inject Presenter() {}
 }
 }
  11. Bottom up migration: before @WithModule(SignScreenModule.class)
 class SignScreen extends RegisterPath {


    @Module(
 addsTo = BuyerFlowModule.class,
 injects = SignView.class)
 public static class SignScreenModule {
 }
 
 @Singleton static class Presenter {
 @Inject Presenter() {}
 }
 }
  12. Bottom up migration: before @WithModule(SignScreenModule.class)
 class SignScreen extends RegisterPath {


    @Module(
 addsTo = BuyerFlowModule.class,
 injects = SignView.class)
 public static class SignScreenModule {
 }
 
 @Singleton static class Presenter {
 @Inject Presenter() {}
 }
 } BuyerFlowModule remains for now…
  13. Bottom up migration: after @WithComponent(SignScreen.Component.class)
 class SignScreen extends RegisterPath {


    @SingleIn(SignScreen.class)
 @Component(dependencies = SignScreen.Dependencies.class)
 public interface Component {
 void inject(SignView view);
 }
 
 @SingleIn(SignScreen.class) static class Presenter {
 @Inject2 Presenter() {}
 }
 }
  14. Bottom up migration: after @WithComponent(SignScreen.Component.class)
 class SignScreen extends RegisterPath {


    @SingleIn(SignScreen.class)
 @Component(dependencies = SignScreen.Dependencies.class)
 public interface Component {
 void inject(SignView view);
 }
 
 @SingleIn(SignScreen.class) static class Presenter {
 @Inject2 Presenter() {}
 }
 }
  15. Bottom up migration: after @WithComponent(SignScreen.Component.class)
 class SignScreen extends RegisterPath {

    public interface Dependencies {
 BuyerFlowPresenter buyerFlowPresenter();
 } @SingleIn(SignScreen.class)
 @Component(dependencies = SignScreen.Dependencies.class)
 public interface Component {
 void inject(SignView view);
 }
 
 …
 }
  16. Bottom up migration: after @WithComponent(SignScreen.Component.class)
 class SignScreen extends RegisterPath {


    public interface Dependencies {
 BuyerFlowPresenter buyerFlowPresenter();
 } class DependenciesImpl implements Dependencies { public DependenciesImpl(ObjectGraph og) { … } BuyerFlowPresenter buyerFlowPresenter() { return og.get(BuyerFlowPresenter.class); } }
 }
  17. Bottom up migration: after @Module(injects = BuyerFlowPresenter.class
 class BuyerFlowModule {

    /* … */ }
 @WithComponent(SignScreen.Component.class)
 class SignScreen extends RegisterPath {
 public interface Dependencies {
 BuyerFlowPresenter buyerFlowPresenter();
 }
 …
 } // elsewhere Component component = DaggerSignScreen_Component .builder()
 .dependencies(new DependenciesImpl(objectGraph))
 .build();