Let's learn what is Dagger2 and how to use it.
"Your dependencies are going to come to you" - Jake Wharton
Dependency Injection let us get rid of dependencies from the business logic.
Here's a really simple example:
public class Car {
private Engine engine;
public Car(Engine engine) {
this.engine = engine;
}
}
public class Main {
private void test() {
Engine engine = new ElectricEngine();
Car car = new Car(engine);
}
}
The engine
of the Car
are given from the outside of the class.
We separate the creation and the use of an object.
Although we are injecting dependencies from the outside, we still need to manually create the instances and connect them. This would be annoying if the dependencies get more complex.
Dependency injection libraries, like Dagger2, usually provides some nice way to handle these problems. Let's get into it.
Specifies the providers. Providers are in charge of making and providing new objects.
Example of a simple module:
@Module
public class CarModule {
@Provides
public Engine provideEngine() {
return new ElectricEngine();
}
}
Methods annotated with @Providers
mean they are providers.
Components are essentially the glue that holds everythings together.
Example of a simple component:
@Component(modules = SimpleModule.class)
public interface SimpleComponent {
void inject(SimpleUseCase useCase);
}
Dagger will use a component to generate the code for us.
In the example above, Dagger creates DaggerSimpleComponent
for us.
This name comes from the name of the component - Dagger + {name of component}
It is possible to inject dependencies using @Inject
annotation, like this:
public class SimpleUseCase {
@Inject
public SimpleData data;
}
In the codes above, data
will be injected using SimpleModule#provideData
@Module
and @Provides
for providing dependencies@Inject
for requesting dependencies (in short, injection)@Component
for bridge between modules and injectionsUsually classes are dependent to some other classes.
Here, the Car
object needs an instance of Engine
in order to be created:
@Module
public class CarModule {
@Provides
public Car provideCar(Engine engine) {
return new Car(engine);
}
@Provides
public Engine provideEngine() {
return new ElectricEngine();
}
}
Dagger creates a graph of dependencies when it parses the module.
It goes like this:
1. I need a car (@Inject)
2. Engine is needed to make a car
3. Create an engine
4. Create a car
It seems pretty simple, but if the dependencies are much more complex, it's really useful.
It is the most common type of injection.
public class User {
@Inject
CarMaker carMaker;
}
It is not that common type of injection, and is rarely used.
public class User {
public void subscribeNewsLetter(@Inject News news) {
news.subscribe(this)
}
}
public class EngineMaker {
}
public class CarMaker {
@Inject
public CarMaker(EngineMaker engineMaker) {
}
}
public class User {
@Inject
CarMaker carMaker;
}
In the codes above, User
requests CarMaker
as a field.
If the constructor of CarMaker
is not annotated with @Inject
, you need to define provider function like this:
public class AppModule {
@Provides
public CarMaker provideCarMaker(EngineMaker engineMaker) {
return new CarMaker();
}
}
If you use constructor injection, you don't need to specify this.
Field Injection and Method Injection are invoked after the instantiation
Therefore, it is possible to define methods something like this:
public void inject(User user);
Internally Dagger creates a class prefixed with _MembersInjector
for this.
(In this case, User_MembersInjector
)
And DaggerAppComponent
uses this class to inject requested dependencies.
// generated code
public void inject(User user) {
injectUser(user);
}
private User injectUser(User instance) {
User_MembersInjector.injectCarMaker(instance, getCarMaker());
return instance;
}
Constructor Injection are invoked before the instantiation
Since there's no dependencies to inject, DaggerAppComponent
does nothing to this line of code.
public void inject(CarMaker carMaker);
// generated code
public void inject(CarMaker carMaker) {}
With @Provides
, the method should return value corresponding to its return type.
@Binds
only works on abstract methods, and
From the docs:
@Binds
methods are a drop-in replacement for@Provides
methods that simply return an injected parameter. Prefer@Binds
because the generated implementation is likely to be more efficient.
So the following module declaration can be replaced with @Binds
@Provides
public Engine provideEngine() {
return new ElectricEngine();
}
// Using @Binds
@Binds
public abstract Engine provideEngine(ElectricEngine engine);
TBD
TBD
TBD
Here are some useful links about Dagger2.