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

Toothpick Bad Practices 🙅 and Nice Tricks 👌

Toothpick Bad Practices 🙅 and Nice Tricks 👌

Presentation to improve Toothpick usage at Groupon

Daniel Molinero Reguera

March 30, 2017
Tweet

More Decks by Daniel Molinero Reguera

Other Decks in Programming

Transcript

  1. SCOPE INJECTING STUFF: GRAPH AND ROOT FACTORIES & MEMBER INJECTORS

    SCOPE LIFECYCLE APPLICATION SCOPE ACTIVITY SCOPE LAZIES & SCOPES CONTEXT & SCOPES SUPPRESS WARNING UNIT TESTING THE TALK
  2. public class DealDetailActivity extends Activity { public static final String

    EXTRA_DEAL_ID = "EXTRA_DEAL_ID"; public static final String EXTRA_SHOW_MAP = "EXTRA_SHOW_MAP"; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); Scope scope = Toothpick.openScope(this); Toothpick.inject(this, scope); ... } SCOPE 1. OPEN SCOPE 2. INJECTING USING SCOPE
  3. public class DealDetailActivity extends Activity { public static final String

    EXTRA_DEAL_ID = "EXTRA_DEAL_ID"; public static final String EXTRA_SHOW_MAP = "EXTRA_SHOW_MAP"; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); Scope scope = Toothpick.openScope(this); Toothpick.inject(this, scope); ... } SCOPE 1. OPEN SCOPE 2. INJECTING USING SCOPE WHAT???? WHY???? WHY???? WHAT????
  4. INJECTING STUFF: GRAPH AND ROOT public class A extends Activity

    { @Inject B b; @Inject C c; } public class B extends Fragment { @Inject D d; @Inject E e; } public class C extends Fragment { @Inject F f; } public class E extends View { @Inject G g; } A D B C E F G
  5. A D B C E F G public class A

    extends Activity { @Inject B b; @Inject C c; public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); Scope scope = Toothpick.openScope(this); Toothpick.inject(this, scope); ... } INJECTING STUFF: GRAPH AND ROOT
  6. A D B C E F G DI root public

    class A extends Activity { @Inject B b; @Inject C c; public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); Scope scope = Toothpick.openScope(this); Toothpick.inject(this, scope); ... } INJECTING STUFF: GRAPH AND ROOT
  7. A D B C E F G DI root 1.

    CREATE 2. INJECT FACTORIES & MEMBER INJECTORS class B$$Factory implements Factory { public B createInstance(Scope scope) { scope = getTargetScope(scope); B b = new B(); memberInjector.inject(b, scope); return b; } } class A$$MemberInjector implements MI { public A inject(A a, Scope scope) { a.b = scope.getInstance(B.class); a.c = scope.getInstance(C.class); } }
  8. A D B C E F G DI root 1.

    CREATE 2. INJECT FACTORIES & MEMBER INJECTORS B C class B$$Factory implements Factory { public B createInstance(Scope scope) { scope = getTargetScope(scope); B b = new B(); memberInjector.inject(b, scope); return b; } } class A$$MemberInjector implements MI { public A inject(A a, Scope scope) { a.b = scope.getInstance(B.class); a.c = scope.getInstance(C.class); } }
  9. A D B C E F G DI root 1.

    CREATE 2. INJECT FACTORIES & MEMBER INJECTORS B C class B$$Factory implements Factory { public B createInstance(Scope scope) { scope = getTargetScope(scope); B b = new B(); memberInjector.inject(b, scope); return b; } } class A$$MemberInjector implements MI { public A inject(A a, Scope scope) { a.b = scope.getInstance(B.class); a.c = scope.getInstance(C.class); } }
  10. A D B C E F G DI root 1.

    CREATE 2. INJECT FACTORIES & MEMBER INJECTORS B C D E F class B$$Factory implements Factory { public B createInstance(Scope scope) { scope = getTargetScope(scope); B b = new B(); memberInjector.inject(b, scope); return b; } } class A$$MemberInjector implements MI { public A inject(A a, Scope scope) { a.b = scope.getInstance(B.class); a.c = scope.getInstance(C.class); } }
  11. A D B C E F G DI root 1.

    CREATE 2. INJECT FACTORIES & MEMBER INJECTORS B C D E F class B$$Factory implements Factory { public B createInstance(Scope scope) { scope = getTargetScope(scope); B b = new B(); memberInjector.inject(b, scope); return b; } } class A$$MemberInjector implements MI { public A inject(A a, Scope scope) { a.b = scope.getInstance(B.class); a.c = scope.getInstance(C.class); } }
  12. A D B C E F G DI root 1.

    CREATE 2. INJECT FACTORIES & MEMBER INJECTORS B C D E F G class B$$Factory implements Factory { public B createInstance(Scope scope) { scope = getTargetScope(scope); B b = new B(); memberInjector.inject(b, scope); return b; } } class A$$MemberInjector implements MI { public A inject(A a, Scope scope) { a.b = scope.getInstance(B.class); a.c = scope.getInstance(C.class); } }
  13. A D B C E F G DI root 1.

    CREATE 2. INJECT FACTORIES & MEMBER INJECTORS B C D E F G class B$$Factory implements Factory { public B createInstance(Scope scope) { scope = getTargetScope(scope); B b = new B(); memberInjector.inject(b, scope); return b; } } class A$$MemberInjector implements MI { public A inject(A a, Scope scope) { a.b = scope.getInstance(B.class); a.c = scope.getInstance(C.class); } }
  14. A D B C E F G DI root public

    class A extends Activity { @Inject B b; @Inject C c; public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); Scope scope = Toothpick.openScope(this); Toothpick.inject(this, scope); ... } FACTORIES & MEMBER INJECTORS
  15. public class DealDetailActivity extends Activity { @Override public void onCreate(Bundle

    savedInstanceState) { super.onCreate(savedInstanceState); Scope scope = Toothpick.openScope(this); Toothpick.closeScope(this); Toothpick.inject(this, scope); ... } } SCOPE LIFECYCLE CLOSED SCOPES CANNOT BE USED
  16. public class DealDetailActivity extends Activity { @Override public void onCreate(Bundle

    savedInstanceState) { super.onCreate(savedInstanceState); Scope scope = Toothpick.openScope(this); Toothpick.closeScope(this); Toothpick.inject(this, scope); ... } } SCOPE LIFECYCLE Crash CLOSED SCOPES CANNOT BE USED
  17. SCOPE LIFECYCLE Crash DO NOT USE ACTIVITY SCOPE AFTER ONDESTROY

    CALLBACKS NETWORK CODE RX ASYNC CODE EVENT BUS LISTENERS
  18. public class A extends Activity { @Inject Lazy<B> b; @Inject

    C c; public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); Scope scope = Toothpick.openScope(this); Toothpick.inject(this, scope); ... } public void onDestroy() { Toothpick.closeScope(this); } public void afterOnDestroy() { b.get().someMethod(); } LAZIES & SCOPES LAZY<B> ACTIVITY
 SCOPE
  19. public class A extends Activity { @Inject Lazy<B> b; @Inject

    C c; public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); Scope scope = Toothpick.openScope(this); Toothpick.inject(this, scope); ... } public void onDestroy() { Toothpick.closeScope(this); } public void afterOnDestroy() { b.get().someMethod(); } LAZIES & SCOPES LAZY<B> ACTIVITY
 SCOPE Crash
  20. public class A extends Activity { @Inject Lazy<B> b; @Inject

    C c; public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); Scope scope = Toothpick.openScope(this); Toothpick.inject(this, scope); ... } public void onDestroy() { Toothpick.closeScope(this); } public void afterOnDestroy() { b.get().someMethod(); } public class A extends Activity { @Inject Lazy<B> b; @Inject C c; public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); Scope scope = Toothpick.openScope(application); Toothpick.inject(this, scope); ... } public void onDestroy() { Toothpick.closeScope(this); } public void afterOnDestroy() { b.get().someMethod(); } LAZIES & SCOPES LAZY<B> ACTIVITY
 SCOPE APPLICATION SCOPE
  21. FACTORIES & MEMBER INJECTORS A D B C E F

    G @Singleton ACTIVITY SCOPE Lazy
  22. FACTORIES & MEMBER INJECTORS A D B C E F

    G @Singleton ACTIVITY SCOPE APPLICATION SCOPE Lazy
  23. Crash DO NOT USE LAZY.GET() AFTER ONDESTROY CALLBACKS NETWORK CODE

    RX ASYNC CODE LAZIES & SCOPES EVENT BUS LISTENERS
  24. CONTEXT & SCOPES public class DealView extends FrameLayout { @Override

    public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); Context context = getContext(); if (!context instanceof Activity) { throw new ThisIsBadException(); } ... } CONTEXT CAN BE DIFFERENT FROM ACTIVITY: CONTEXTWRAPPER
  25. CONTEXT & SCOPES public class DealView extends FrameLayout { @Override

    public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); Context context = getContext(); if (!context instanceof Activity) { throw new ThisIsBadException(); } ... } Crash CONTEXT CAN BE DIFFERENT FROM ACTIVITY: CONTEXTWRAPPER
  26. public class DealView extends FrameLayout { @Override public void onCreate(Bundle

    savedInstanceState) { super.onCreate(savedInstanceState); Context context = getContext(); Scope scope = Toothpick.openScope(context); ... } } ACTIVITY SCOPE != CONTEXT SCOPE CONTEXT & SCOPES
  27. public class DealView extends FrameLayout { @Override public void onCreate(Bundle

    savedInstanceState) { super.onCreate(savedInstanceState); Context context = getContext(); Scope scope = ContextScopeFinder.getScope(context); ... } } CONTEXT SCOPE FINDER FINDS THE ACTIVITY SCOPE FOR US CONTEXT & SCOPES
  28. GtgDealCardViewHandler.java:17: warning: The class com.groupon.gtg.mga.customview.ildcard.GtgDealCardViewHa ndler has injected fields but

    has no injected constructor, and no public default constructor. Toothpick can't create a factory for it. SUPPRESS WARNING
  29. SUPPRESS WARNING class B$$Factory implements Factory { public B createInstance(Scope

    scope) { scope = getTargetScope(scope); B b = new B(); memberInjector.inject(b, scope); return b; } } class B$$MemberInjector implements MI { public B inject(B b, Scope scope) { b.d = scope.getInstance(D.class); b.e = scope.getInstance(E.class); } } public class B extends Fragment { @Inject D d; @Inject E e; private B(F f) { Toothpick.inject(this, scope); } }
  30. SUPPRESS WARNING class B$$Factory implements Factory { public B createInstance(Scope

    scope) { scope = getTargetScope(scope); B b = new B(); memberInjector.inject(b, scope); return b; } } class B$$MemberInjector implements MI { public B inject(B b, Scope scope) { b.d = scope.getInstance(D.class); b.e = scope.getInstance(E.class); } } X public class B extends Fragment { @Inject D d; @Inject E e; private B(F f) { Toothpick.inject(this, scope); } }
  31. SUPPRESS WARNING @SuppressWarnings(“injectable”) public class B extends Fragment { @Inject

    D d; @Inject E e; private B(F f) { Toothpick.inject(this, scope); } } class B$$MemberInjector implements MI { public B inject(B b, Scope scope) { b.d = scope.getInstance(D.class); b.e = scope.getInstance(E.class); } } D B E DI root
  32. UNIT TESTING public class B extends Fragment { @Inject D

    d; @Inject E e; public B() { Toothpick.inject(this, scope); } } D B E G
  33. UNIT TESTING public class B extends Fragment { @Inject D

    d; @Inject E e; public B() { Toothpick.inject(this, scope); } } MOCK ALL YOUR DEPENDENCIES
  34. UNIT TESTING public class B extends Fragment { @Inject D

    d; @Inject E e; public B() { Toothpick.inject(this, scope); } } MOCK ALL YOUR DEPENDENCIES public class BTest extends EasyMockSupport { @Rule public EasyMockRule easyMockRule; @Rule public ToothpickRule toothpickRule; @Mock D d; @Mock E e; }
  35. public class BTest extends EasyMockSupport { @Rule public EasyMockRule easyMockRule;

    @Rule public ToothpickRule toothpickRule; @Mock D d; @Mock E e; } UNIT TESTING D B E D B E G
  36. BAD PRACTICES 1. DO NOT USE ACTIVITY SCOPE AFTER ONDESTROY

    2. DO NOT CALL LAZY.GET() AFTER ONDESTROY IF INJECTED USING ACTIVITY SCOPE 3. DO NOT OPEN SCOPE USING CONTEXT: CONTEXTSCOPEFINDER 4. USE SUPPRESSWARNING FOR DI ROOTS 5. MOCK ALL YOUR DEPENDENCIES