Dependency Injection on Android
-
Upload
joan-puig-sanz -
Category
Technology
-
view
1.515 -
download
1
description
Transcript of Dependency Injection on Android
Dependency Injec+on on Android
By Joan Puig Sanz
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
//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
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
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
SIMPLE EXAMPLE Factory paPern
5
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
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
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
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
Implementa+on drawbacks
10
• 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
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;
@Injectpublic HotMachine(Heater heater){ this.heater = heater;}public void start(){ heater.on(); //Other stuff}
} !
11
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
ANDROID AND DEPENDENCY INJECTION
RoboGuice and Dagger
13
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
Example -‐ KitKat Machine
• Chocolate • Cookie • Machine – Heater – Mold
Chocolate
Cookie
Machine
Heater
Mold
The source code can be found here: hEp://beyondar.com/di.html
15
Example -‐ KitKat Machine: Steps
1. Define dependencies 2. Create modules 3. Prepare the graph 4. Inject dependencies
16
Example – KitKat Machine: Dependencies
Chocolate
ChocolateWithMilk
Cookie
TastyCookie
Heater
ElectricHeater
Mold
KitKatMold
17
Example – KitKat Machine: Dependencies
KitKatMachine Heater Mold
Machine
void makeKitKat(Chocolate, Cookie)
18
Example – KitKat Machine: Dependencies
!public interface Cookie {
public void doWhatCookiesDo();}public class TastyCookie implements Cookie{
@Injectpublic TastyCookie(){ print("New cookie");}
public void doWhatCookiesDo(){ print("Yummie yummie”);}
} !
Cookie
void doWhatCookiesDo()
TastyCookie
19
Example – KitKat Machine: Dependencies
public interface Machine {public void makeKitKat(Chocolate chocolate, Cookie cookie);
} !
@Singletonpublic 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”); }
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”); }
RoboGuice Dagger
20
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>{
@Overridepublic Chocolate get() { return new ChocolateWithMilk();}
}
Dagger @Modulepublic 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
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
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
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
OTHER STUFF TO INJECT Inject inject inject…
25
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
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
SIDE BY SIDE Go!
28
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
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
beyondar.com
@joanpuigsanz
/joanpuigsanz
31