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

C716975ef0b4ed35553572ff44ea1bed?s=128

Daniel Molinero Reguera

March 30, 2017
Tweet

Transcript

  1. BAD PRACTICES AND NICE TRICKS " TOOTHPICK

  2. 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
  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
  4. 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????
  5. SCOPE 1. TP INJECT TP # inject A

  6. SCOPE 1. TP INJECT TP # inject A SCOPE 2.

    TP USES SCOPE TO INJECT
  7. SCOPE 1. TP INJECT TP # inject A SCOPE 2.

    TP USES SCOPE TO INJECT
  8. SCOPE MAKES THE INJECTIONS SCOPE

  9. INJECTING STUFF: GRAPH AND ROOT A D B C E

    F G
  10. 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
  11. 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
  12. 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
  13. 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); } }
  14. 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); } }
  15. 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); } }
  16. 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); } }
  17. 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); } }
  18. 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); } }
  19. 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); } }
  20. 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
  21. SCOPE LIFECYCLE APPLICATIONโ€จ SCOPE

  22. SCOPE LIFECYCLE APPLICATIONโ€จ SCOPE ACTIVITYโ€จ SCOPE

  23. SCOPE LIFECYCLE APPLICATIONโ€จ SCOPE

  24. 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
  25. 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
  26. SCOPE LIFECYCLE Crash DO NOT USE ACTIVITY SCOPE AFTER ONDESTROY

    CALLBACKS NETWORK CODE RX ASYNC CODE EVENT BUS LISTENERS
  27. SCOPE LIFECYCLE

  28. LAZIES & SCOPES %

  29. 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
  30. 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
  31. 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
  32. APPLICATIONโ€จ SCOPE ACTIVITYโ€จ SCOPE APP SINGLETONS ACTIVITY SINGLETONS LAZIES &

    SCOPES
  33. FACTORIES & MEMBER INJECTORS A D B C E F

    G @Singleton Lazy
  34. FACTORIES & MEMBER INJECTORS A D B C E F

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

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

    RX ASYNC CODE LAZIES & SCOPES EVENT BUS LISTENERS
  37. LAZIES & SCOPES

  38. 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
  39. 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
  40. 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
  41. 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
  42. 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
  43. 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); } }
  44. 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); } }
  45. 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
  46. UNIT TESTING public class B extends Fragment { @Inject D

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

    d; @Inject E e; public B() { Toothpick.inject(this, scope); } } MOCK ALL YOUR DEPENDENCIES
  48. 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; }
  49. 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
  50. 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
  51. BAD PRACTICES AND NICE TRICKS " TOOTHPICK