Dependency injection is a design pattern used to decouple parts of your application from each other. It pushes up dependencies "farther up the chain," and effectively forces your code to follow the Single Responsibility Principle, which we will lightly touch here. This has a number of benefits, such as having more maintainable code, and being easier to unit test.
For example, let's say we want an application that makes Sushi for us (wouldn't that be nice).
For example, let's say we want an application that makes Sushi for us (wouldn't that be nice).
public class Sushi {
private Rice mRice;
private Fish mFish;
public Sushi() {
mRice = new Rice();
mFish = new Fish();
//Other sushi-making steps go here
}
}
Here we see that whenever we make the call 'new Sushi()', we see that we are also making WhiteRice, and Salmon. What happens after we call the constructor and the code breaks (Sushi turns out bad)? We don't know whether it broke when it was making the actual Sushi or when it was trying to make the WhiteRice or Salmon. Better code would look like this:
public class Sushi {
private Rice mRice;
private Fish mFish;
public Sushi(Rice rice, Fish fish) {
mRice = rice;
mFish = fish;
//Other sushi-making steps go here
}
}
With code like this, we are letting the construction of Rice and Fish be someone else's problem, which makes it easier to independently test if and where the code is breaking. This makes it so that the constructor for Sushi ONLY knows how to construct Sushi, and nothing else.
That is the basics of Dependency Injection, now I will jump into a super useful library for Android called Dagger. I know, I know, I always use libraries with Square... Oh well, they are too good to not use.
In the Android docs it says to avoid dependency injection since other DI libraries such as Guice use reflection, which is pretty slow on Android systems. Dagger runs a lot of code during compile time, and mostly avoids using reflection.
Using Dagger, our Sushi class would look something like this:
That is the basics of Dependency Injection, now I will jump into a super useful library for Android called Dagger. I know, I know, I always use libraries with Square... Oh well, they are too good to not use.
In the Android docs it says to avoid dependency injection since other DI libraries such as Guice use reflection, which is pretty slow on Android systems. Dagger runs a lot of code during compile time, and mostly avoids using reflection.
Using Dagger, our Sushi class would look something like this:
public class Sushi {
private Rice mRice;
private Fish mFish;
@Inject
public Sushi(Rice rice, Fish fish) {
mRice = rice;
mFish = fish;
//Other sushi-making steps go here
}
}
After that, we need to set up our provider modules so that Dagger will know how to make our classes. We need to add @Module and specify where our class is injecting.
@Module(
injects = {
SushiActivity.class
}
)
public class SushiModule {
@Provides
Rice provideRice() {
return new Rice();
}
@Provides
Fish provideFish() {
return new Fish();
}
@Provides
Sushi provideSushi(Fish fish, Sushi sushi) {
return new Sushi(fish, sushi);
}
}
Here, we are providing the Rice and Fish injector, which are used in the Sushi injector. We also specify that we will be injecting one of these providers into SushiActivity.class. With this, you might be thinking "Yay! All done!". Not quite! We still need to set up our App Module so and include our SushiModule so that Dagger can do all of its magic during compile time.
@Module(
injects = {
SushiApp.class
},
includes = {
SushiModule.class
}
)
public class AndroidModule {
private final SushiApp application;
public AndroidModule(SushiApp sushiApp) {
application = sushiApp;
}
}
After this, we need to create our ObjectGraph in our Application class so that it Dagger construct its dependency graph. We can do this by doing:
public class SushiApp extends Application {
private ObjectGraph mObjectGraph;
@Override
public void onCreate() {
super.onCreate();
mObjectGraph = ObjectGraph.create(getModules().toArray());
}
private List<Object> getModules() {
return Arrays.<Object>asList(
new AndroidModule(this)
);
}
public void inject(Object object) {
mObjectGraph.inject(object);
}
}
I know, its a lot of code. Bear with me, Dagger is now pretty much set up, we just need to inject it in our Activity in order to actually use it.
public class SushiActivity extends Activity {
@Inject
Sushi mSushi;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
((SushiApp)getApplication()).inject(this);
mSushi.eat(); //nomnom
}
}
Finally done! As you can see, it does take a bit if work to set your application up with Dagger, however the benefits definitely outweigh the costs.
Please let me know if I have gotten somethings wrong or something is unclear, I am also just starting to play around with DI and dagger.
Side note: Based on OOP standards, Sushi class isn't supposed to have the Sushi#eat() method. It should be something like Person.eat(mSushi). I am only doing this just to show that you can access the object right after the call to inject(). :-D
Please let me know if I have gotten somethings wrong or something is unclear, I am also just starting to play around with DI and dagger.
Side note: Based on OOP standards, Sushi class isn't supposed to have the Sushi#eat() method. It should be something like Person.eat(mSushi). I am only doing this just to show that you can access the object right after the call to inject(). :-D