Dependency Injection on Android

2,513 views

Published on

How to use Dependency Injection on Android using RoboGuice and Dagger. This presentation was shown at the DroidCon NL 2013

Published in: Technology, Business

Dependency Injection on Android

  1. 1. Dependency  Injec+on  on   Android   By  Joan  Puig  Sanz  
  2. 2. About  Joan  Puig  Sanz   •  Electrical  Engineer   •  So<ware/hardware  fan  :)   •  Open  Source  Projects:   –  ServDroid:  Simple  web  Server   –  BeyondAR  Framework:  Augmented  Reality  framework   beyondar.com   1  
  3. 3. //TODO:   1.  What  is  Dependency  Injec+on?   2.  Simple  Example:  Factory  PaPern   3.  Android  and  Dependency  Injec+on   –  KitKatMachine  example  with  RoboGuice  and   Dagger   –  Compare  RoboGuice  and  Dagger   4.  Conclusions   2  
  4. 4. What  is  Dependency  Injec+on?   “Dependency  injec+on  is  a  so<ware  design  paPern   that  allows  the  removal  of  hard-­‐coded  dependencies   and  makes  it  possible  to  change  them,  whether  at   run-­‐+me  or  compile-­‐+me”     Dependency  Injec+on  is  like  ordering  a  hawaiian   pizza  but  specifying  the  ingredients,  for  instance,   instead  of  ham  we  want    pepperoni.       3  
  5. 5. What  does  Dependency  Injec+on  do?   •  It  improves  the  code:  reusable,  modulable  and   testability   •  It  allows  the  developer  to  focus  on  the  code   that  adds  a  plus  value  to  the  app:   –  No  need  to  worry  about  Singletons,  Factories  and   others.   4  
  6. 6. Factory  paPern   SIMPLE  EXAMPLE   5  
  7. 7. The  Factory  PaPern   public interface Heater { public void on(); public void off(); public boolean isHot(); } public class ElectricHeater implements Heater { private boolean heating; public void on() { heating = true; } public void off() { heating = false; } public boolean isHot() { return heating; } }! Heater     void  on()   void  off()   boolean  isHot()   ElectricHeater   6  
  8. 8. The  Factory  PaPern  –  wri+ng  it   public class HeaterFactory { private HeaterFactory () {} private static Heater instance = new ElectricHeater(); public static Heater getInstance() { return instance; } public static void setInstance(Heater heater) { instance = heater; } } public class HotMachine{ public void start(){ heater = HeaterFactory.getInstance(); heater.on(); // Other stuff } }! 7  
  9. 9. The  Factory  PaPern  –  Unit  Test   public void testHeater(){ Heater previous = HeaterFactory.getInstance(); try{ Heater mock = new mockHeater(); HeaterFactory.setInstance(mock); HotMachine machine = new HotMachine(); machine.start(); assertTrue(mock.isHot()); }finally{ HeaterFactory.setInstance(previous); } } 8  
  10. 10. Implementa+on  drawbacks   •  For  every  dependency  we  make  we  need  to   write  the  same  code.   •  Our  test  had  to  pass  the  mock  to  the  factory   and  then  clean  up  a<erwards.   •  You  cannot  determine  if  HotMachine  depends   on  Heater  at  first  sight   •  It  could  be  difficult  to  reuse  HotMachine  in  a   different  context     9  
  11. 11. Implementa+on  drawbacks   •  For  every  dependency  we  make  we  need  to   write  the  same  code.   •  Our  test  had  to  pass  the  mock  to  the  factory   and  then  clean  up  a<erwards.   •  You  cannot  determine  if  Machine  depends  on   Heater  at  first  sight   •  It  could  be  difficult  to  reuse  Machine  in  a   different  context     10  
  12. 12. Factory  PaPern  with  DI   •  We  need  to  define  a  module  specifying  what  are  we  going  to  inject.   –  Heater  as  ElectricHeater   –  We  also  can  define  how.   public class HotMachine{ private final Heater heater; @Inject public HotMachine(Heater heater){ this.heater = heater; } public void start(){ heater.on(); //Other stuff } }! 11  
  13. 13. Factory  PaPern  with  DI  –  Unit  Tes+ng   ! public void testHeater(){ Heater mock= new mockHeater(); HotMachine machine = new HotMachine(mock); machine.start() assertTrue(mock.isHot()); }! 12  
  14. 14. RoboGuice  and  Dagger   ANDROID  AND  DEPENDENCY   INJECTION   13  
  15. 15. Android   •  We  can  create  one  ourselves…  but  it  is  not   needed  and  it  is  a  lot  of  work   •  The  best  ones  are  RoboGuice  and  Dagger   –  RoboGuice  does  injec+on  during  run+me   •  hPps://github.com/roboguice/roboguice   –  Dagger  Generates  code   •  hPps://github.com/square/dagger/   14  
  16. 16. Example  -­‐  KitKat  Machine   •  Chocolate   •  Cookie   •  Machine   Chocolate   Heater   Machine   –  Heater   –  Mold   Cookie   Mold   The  source  code  can  be  found  here:  hEp://beyondar.com/di.html   15  
  17. 17. Example  -­‐  KitKat  Machine:   Steps   1.  2.  3.  4.  Define  dependencies   Create  modules   Prepare  the  graph   Inject  dependencies   16  
  18. 18. Example  –  KitKat  Machine:   Dependencies   Chocolate   Cookie   Heater   Mold   ChocolateWithMilk   TastyCookie   ElectricHeater   KitKatMold   17  
  19. 19. Example  –  KitKat  Machine:   Dependencies   Machine     void makeKitKat(Chocolate, Cookie)   Mold   KitKatMachine   Heater   18  
  20. 20. Example  –  KitKat  Machine:   Dependencies   ! public interface Cookie { public void doWhatCookiesDo(); } public class TastyCookie implements Cookie{ @Inject public TastyCookie(){ print("New cookie"); } public void doWhatCookiesDo(){ print("Yummie yummie”); } }! Cookie     void doWhatCookiesDo() TastyCookie   19  
  21. 21. Example  –  KitKat  Machine:   Dependencies   public interface Machine { public void makeKitKat(Chocolate chocolate, Cookie cookie); }! RoboGuice   @Singleton public class KitKatMachine implements Machine{ private Mold mold; private Heater heater; @Inject public KitKatMachine(Mold mold, Heater heater) { this.mold = mold; this.heater = heater; } public void makeKitKat(Chocolate chocolate, Cookie cookie){ heater.on(); Chocolate meltedChocolate = heater.melt(chocolate); mold.putToghether(cookie, chocolate); print (”Kitkat ready”); } Dagger   public class KitKatMachine implements Machine{ private Mold mold; private Lazy<Heater> heater; @Inject public KitKatMachine(Mold mold, Lazy<Heater> heater) { this.mold = mold; this.heater = heater; } public void makeKitKat(Chocolate chocolate, Cookie cookie){ heater.get.on(); Chocolate meltedChocolate = heater.get().melt(chocolate); mold.putToghether(cookie, chocolate); print (”Kitkat ready”); } 20  
  22. 22. Example  -­‐  KitKat  Machine:   Modules   RoboGuice   public class kitkatModule extends AbstractModule { protected void configure(){ bind(Cookie.class). toProvider(CookieProvider.class); bind(Chocolate.class). toProvider(ChocolateProvider.class); bind(Machine.class). to(KitkatMachine.class) } } public class ChocolateProvider implements Provider<Chocolate>{ @Override public Chocolate get() { return new ChocolateWithMilk(); } } Dagger   @Module public class KitkatModule { @Provides public Chocolate provideChocolate() { return new ChocolateWithMilk(); } @Provides public Cookie provideCookie(){ return new TastyCookie(); } @Provides @Singleton public Machine } providesMachine(KitKatMachine machine){ return machine; } 21  
  23. 23. Example  -­‐  KitKat  Machine:   Preparing  the  Graph   RoboGuice   public class KitKatMachineApplication extends Application { public void onCreate() { super.onCreate(); RoboGuice.setBaseApplicationInjector (this, Stage.PRODUCTION, getModules()); } protected Module[] getModules() { List<Module> modules = new ArrayList<Module>(); modules.add( RoboGuice.newDefaultRoboModule(this)); modules.add(new IngredientsModule()); modules.add(new MachineModule()); return (Module[]) modules.toArray(new Module[modules.size()]); } } Dagger   public class KitKatMachineApplication extends Application{ private ObjectGraph graph; public void onCreate() { super.onCreate();a graph = ObjectGraph.create( getModules().toArray()); } protected List<Object> getModules() { ArrayList<Object> modules = new ArrayList<Object>(); modules.add(new AndroidModule(this)); modules.add(new IngredientsModule()); modules.add(new MachineModule()); return modules; } public void inject(Object object) { graph.inject(object); } public ObjectGraph getApplicationGraph() { return graph; } 22  
  24. 24. Example  -­‐  KitKat  Machine:   Injec+ng  dependencies   public class HomeFragment extends #########{ @Inject Provider<Chocolate> chocolate; @Inject Provider<Cookie> cookie; @Inject Machine machine; // With Dagger we can use Lazy<Machine> to create a lazy singleton onResume(){ super.onResume() machine.makeKitKat(chocolate.get(), cookie.get()); } } #########   •  RoboGuice:  RoboFragment   •  Dagger:  BaseFragment  (your  custom  BaseFragment)   23  
  25. 25. Example  -­‐  KitKat  Machine:   Injec+ng  dependencies  -­‐  Dagger   public class BaseFragment extends Fragment { public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); // Perform injection so that when this call returns all // dependencies will be available for use. ((KitKatMachineApplication) getApplication()).inject(this); } }! •  The  same  goes  for  the  Ac+vity.   •  It  is  highly  customizable   –  For  Instance:  It  is  possible  to  personalize  the  modules  that  we  want  to   inject  into  the  Ac+vity  (crea+ng  a  derivate  graph)   24  
  26. 26. Inject  inject  inject…   OTHER  STUFF  TO  INJECT   25  
  27. 27. Injec+ng  other  stuff  -­‐  Qualifiers   •  It  is  also  possible  to  inject  Qualifiers:   -­‐  Module:     @Provides @Named("app_version") public String getAppVersionCode(Context context) { return String.valueOf(getPackageInfo(context).versionCode); }!   -­‐  InjecSng:   ! @Inject @Named("app_version") String version;! ! 26  
  28. 28. Injec+ng  other  stuff  -­‐  RoboGuice   •  With  RoboGuice  we  can  inject  views,   preferences,  fragments,  extras  and  resources:     @InjectView(R.id.myTextView) private TextView myTextView; @InjectResource(R.string.app_name) private String appName;! ! •  We  also  can  inject  Context,  Inflater,  Services   and  some  other  Android  Objects.   –  Check  out  DefaultRoboModule.java  for  more  informa+on   27  
  29. 29. Go!   SIDE  BY  SIDE   28  
  30. 30. Side  by  side   RoboGuice   •  Can  inject  dependencies  in   private  fields   •  Method  Injec+on  supported   •  Configure  Proguard  is  hard   (doable)   •  Ready  to  inject  a  lot  of   Android  stuff   •  It  uses  Guice  which  is  more   server  related   •  Very  cool  framework   Dagger   •  Method  Injec+on  not  supported…   for  now   •  Faster.  In  a  big  app  it  can  reduce   the  startup  +me  in  a  few  seconds   •  Configure  Proguard  is  a  pain  in   the  ass   •  If  you  want  to  inject  Android  stuff   you  need  to  write  the  code  to  do   it   •  It  forces  developers  to  write  a  bit   more  code   •  Remember  that  it  generates   code!   •  Very  cool  framework   29  
  31. 31. Conclusions   •  Dependency  injec+ons  allows  the  developer  to  spend   +me  on  the  important  parts   •  It  makes  the  applica+on  more  modular   •  It  helps  tes+ng  your  applica+on   •  It  makes  your  code  less  like  spagheq,  ravioli,  lasagna   and  other  Italian  foods   •  The  developer  needs  to  understand  the  DI  framework   •  Easier  to  implement  at  the  beginning  than  at  the  end   •  Very  cool  frameworks  :)   30  
  32. 32. beyondar.com   @joanpuigsanz   /joanpuigsanz   31  

×