7.
More About Injection
Written by Massimo Carli
In the previous chapter, you started using Dagger with a very basic Server-Repository example. As you remember from the first chapter, the code you implemented uses a simple dependency between the Server and a Repository called loosely coupled. You represent this dependency with the UML diagram in Figure 7.1:
You learned how to tell Dagger how to generate the factory for the instances in the dependency graph using the @Component annotation. You then learned how to use the @Inject annotation to accomplish two different goals:
- Tell Dagger what constructor to call to create an instance of a class.
- Mark properties as targets for injection.
If the type of dependency is an abstraction, like an interface, Dagger needs some additional information. You provide this information by using a @Module containing some functions that you annotate with @Provides. This way, you tell Dagger which function to invoke to get an instance of a class for a specific type. Luckily, Dagger is a good listener. :]
You learned that the @Inject, @Component, @Module and @Provides annotations are all you need to implement dependency injection in your app with Dagger. The rest of the annotations let you improve performance when generating and executing the code.
In this chapter, you’ll discover even more about dependency injection with Dagger. You’ll learn how to:
- Deal with constructor, field and method injection with Dagger.
- Simplify the implementation of
@Moduleby using@Bindsin cases when you have an abstraction and its implementation. You saw how this works in theRepositoryandFakeRepositoryexample. - Use
@Singletonfor the first time to solve a very common problem.
There’s still a lot to do. Prepare to have some more fun!
Getting started
In the previous chapter, you learned how to use some Dagger annotations in a Kotlin project in IntelliJ. In this chapter, you’ll return to Android with the RaySequence app. This is a very simple app that allows you to display a numeric value of a sequence on the screen every time you press a Button.
To get started, use Android Studio to open the RaySequence project in the starter folder of the materials for this chapter. Build and run and you’ll get the screen shown in Figure 7.2:
Note: Don’t worry about the Busso App. In a few chapters, you’ll migrate it to Dagger and everything will seem very easy to you.
At the moment, the app doesn’t work: When you click the Button, nothing happens. Figure 7.3 shows the file structure of the app:
As you see, the app uses the same mvp library you saw in the previous chapters and the implementations for Model, ViewBinder and Presenter have already been done. However, you still need to connect the dots.
Before doing this, take a quick look at the model. Open SequenceGenerator.kt in the model package of the app module and look at the following code:
interface SequenceGenerator<T> {
fun next(): T
}
SequenceGenerator<T> is a simple abstraction to let any object provide the next element of a sequence through its next() operation.
Note: The Kotlin standard library already provides the
Sequence<T>interface, which is similar toSequenceGenerator<T>and has some utility builders like thesequence()higher-order function. However, usingSequence<T>requires you to define anInterator<T>, which makes the code a bit more complex with no gain in the context of dependency injection.
In the same model package are two SequenceGenerator<T> implementations. NaturalSequenceGenerator.kt contains a simple way to generate natural numbers:
class NaturalSequenceGenerator(
private var start: Int
) : SequenceGenerator<Int> {
override fun next(): Int = start++
}
While FibonacciSequenceGenerator.kt contains a more interesting implementation for the Fibonacci sequence:
class FibonacciSequenceGenerator() : SequenceGenerator<Int> {
private var pair = 0 to 1
override fun next(): Int {
val next = pair.first
pair = pair.second to pair.first + pair.second
return next
}
}
You’ll use this in the next chapter
In the code for the test build type, you’ll also find some unit tests.
The gradle.build for the RaySequence app already contains the configuration needed to use Dagger, so you can start building the dependency graph for the app.
Note: To save space, some of the code for this project isn’t printed out. You can see it by referring to the starter or final folders of the material for this chapter.
Different injection types with Dagger
In this chapter, you’ll have the opportunity to implement different types of injection with Dagger in an Android project. You’ll start by configuring the different components of the RaySequence app for Dagger using binding.
Keep in mind that binding means: connecting different components according to their dependencies.
Then, you’ll bind the following components:
SequenceViewBinderSequencePresenterMainActivity
Finally, you’ll provide all the information Dagger needs to make RaySequence work.
Ready to get started? Jump right in!
Binding the ViewBinder with constructor injection
Open SequenceViewBinderImpl.kt in the view package of the app module and look at the following code:
class SequenceViewBinderImpl(
// HERE
private val sequenceViewListener: SequenceViewBinder.Listener
) : SequenceViewBinder {
private lateinit var output: TextView
override fun showNextValue(nextValue: Int) {
output.text = "$nextValue"
}
override fun init(rootView: MainActivity) {
output = rootView.findViewById(R.id.sequence_output_textview)
rootView.findViewById<Button>(R.id.next_value_button)
.setOnClickListener {
sequenceViewListener.onNextValuePressed()
}
}
}
As you see, SequenceViewBinderImpl depends on the implementation of SequenceViewBinder.Listener you pass as the primary constructor parameter. This is an example of constructor injection.
You also know that SequenceViewBinderImpl implements SequenceViewBinder. That’s also the type you’ll use to define the dependency. In this case, Dagger offers you two different options to create an instance of SequenceViewBinderImpl. You can:
- Invoke the constructor directly.
- Delegate the creation of the instance to Dagger.
Both solutions require you to define a @Module because you want to use SequenceViewBinder as the type of the instance in the dependency.
Whichever method you choose, the first step is the same: Create a new di package with a new file named AppModule.kt in it. What you put inside depends on the approach you decide to take.
Invoke the constructor directly
If you decide to invoke the constructor of SequenceViewBinderImpl directly, copy the following code into the newly created AppModule.kt:
// 1
@Module
object AppModule {
// 2
@Provides
fun provideSequenceViewBinder(
// 3
viewBinderListener: SequenceViewBinder.Listener
// 4
): SequenceViewBinder = SequenceViewBinderImpl(viewBinderListener)
}
In these few lines of code, there are many interesting points:
- You define a
@Moduleto provide Dagger with some of the information it needs to build the dependency graph for the RaySequence app. - Using
@Provides, you tell Dagger which function to invoke to provide the objects of typeSequenceViewBinder. The return type of the function is what matters here. -
provideSequenceViewBinder(), whose name is only important for readability, has a parameter of typeSequenceViewBinder.Listener.SequenceViewBinderImplrequires this in its primary constructor. - You use the function parameter
viewBinderListenerto create the instance ofSequenceViewBinderImplto return. This is where you explicitly create the instance ofSequenceViewBinderImpl.
In a configuration where an instance of type SequenceViewBinder is required, Dagger will invoke provideSequenceViewBinder(), passing the reference to an implementation of SequenceViewBinder.Listener — which you still need to configure. In this case, it’s important to note that Dagger doesn’t need to know anything about how to create the instance of SequenceViewBinderImpl.
Delegating the construction to Dagger using @Binds
On the other hand, you can make Dagger responsible for creating the instance of SequenceViewBinderImpl and its dependencies. In this case, you need to tell Dagger two things:
- That
SequenceViewBinderImplis the class to use the implementation for a dependency of typeSequenceViewBinder. - How to create an instance of
SequenceViewBinderImpl.
To accomplish the first task, replace the contents of AppModule.kt with the following code:
// 1
@Module
object AppModule {
// 2
@Module
interface Bindings {
// 3
@Binds
fun bindSequenceViewBinder(impl: SequenceViewBinderImpl): SequenceViewBinder
}
}
As you can see, there’s less code here than in the previous case. Here you:
-
Define a
@Moduleto give Dagger some of the information it needs to build the dependency graph, as in the previous case. -
Create a
Bindingsinterface annotated as@Modulethat will contain all the binding definitions. -
Use
@Bindsto bind the type of abstraction to the implementation to use. The type of the abstraction is the return type of the binding function. The type of the implementation is the type of the unique parameter for the same function. In this case, you’re telling Dagger that whenever it needs an object of typeSequenceViewBinder, it needs to return an instance ofSequenceViewBinderImpl.
Note: Using an internal interface for the definition of
@Bindsis just the convention this book uses. It’s a simple way to keep the concrete@Providesfunctions in one concrete object and the abstract@Bindsfunctions in another, but still in the same file. However, you’ll see other conventions in your future work with Dagger.
When you ask Dagger to create an instance of SequenceViewBinderImpl for you, you need to tell it how to do so. As you learned in the previous chapter, you do that by using @Inject.
Open SequenceViewBinderImpl.kt and apply the following change:
class SequenceViewBinderImpl @Inject constructor( // HERE
private val sequenceViewListener: SequenceViewBinder.Listener
) : SequenceViewBinder {
// ...
}
In both cases, Dagger knows what to do to provide an instance of SequenceViewBinder, but one piece of information is still missing: It doesn’t know how to resolve the instance for the type SequenceViewBinder.Listener. It needs this as a parameter for provideSequenceViewBinder() in the first scenario, and as a constructor parameter for SequenceViewBinderImpl in the second. To solve this problem, you need to work on the Presenter.
It’s interesting to note that, right now, you can build the app with no errors. That’s because you don’t have any @Component yet so Dagger doesn’t have anything to do.
Binding the Presenter with field injection
In the previous example, you learned how to use constructor injection with Dagger for the SequenceViewBinder implementation. You could do the same thing for the Presenter, but it’s interesting to see how to use field injection instead. To do this, open SequencePresenterImpl.kt in the presenter package and look at the following code:
class SequencePresenterImpl : BasePresenter<MainActivity,
SequenceViewBinder>(),
SequencePresenter {
lateinit var sequenceModel: SequenceGenerator<Int> // HERE
override fun displayNextValue() {
useViewBinder {
showNextValue(sequenceModel.next())
}
}
override fun onNextValuePressed() {
displayNextValue()
}
}
This is the code for SequencePresenterImpl, which is the implementation for SequencePresenter. This is the Presenter for the MainActivity of the RaySequence app. In this code, you see how SequencePresenterImpl depends on an implementation of SequenceGenerator<Int>.
In theory, SequencePresenterImpl also depends on the SequenceViewBinder implementation. As you’ll see in a few moments, however, this is something you can solve using the bind()/unbind() functions, which the class inherits from BasePresenter.
Inside SequencePresenter.kt, you’ll find this code, which tells you that SequencePresenter IS-A SequenceViewBinder.Listener.
interface SequencePresenter :
Presenter<MainActivity, SequenceViewBinder>,
SequenceViewBinder.Listener { // HERE
fun displayNextValue()
}
You can represent these relations using the UML diagram in Figure 7.4:
Here, you need to tell Dagger to:
- Use
SequencePresenterImplas an implementation for theSequencePresenterabstraction. - Inject the dependency to the
SequenceGenerator<Int>implementation intoSequencePresenterImplinsequenceModel. - Create the instance of
SequenceGenerator<Int>to use as a model. - Use the
SequencePresenterimplementation whenever it needs an object of typeSequenceViewBinder.Listener.
You already know how to do all this. You’ll put that knowledge to work next.
Using @Binds for the Presenter
To bind SequencePresenterImpl to SequencePresenter, you’ll use @Binds. Open AppModule.kt and add the following definition, leaving the rest as it is:
@Module
object AppModule {
@Module
interface Bindings {
// ...
@Binds
fun bindSequencePresenter(impl: SequencePresenterImpl): SequencePresenter // HERE
}
}
In this code, you’re telling Dagger to create an instance of SequencePresenterImpl every time it needs an object of type SequencePresenter.
Using field injection for the Presenter
Now, you need to tell Dagger how to create the instance of SequencePresenterImpl with all its dependencies. Open SequencePresenterImpl.kt and change it to this:
// 1
class SequencePresenterImpl @Inject constructor(
) : BasePresenter<MainActivity,
SequenceViewBinder>(),
SequencePresenter {
// 2
@Inject
lateinit var sequenceModel: SequenceGenerator<Int>
// ...
}
In this code, you use @Inject for two different purposes:
- Identifying the constructor to invoke to create the instance of type
SequencePresenterImpl. - Marking
sequenceModelas the target to be injected with an object of typeSequenceGenerator<Int>, which is still an abstraction.
This is an example of field injection in Dagger.
Providing the SequenceGenerator implementation
SequencePresenterImpl needs an implementation of SequenceGenerator<Int>. You could use @Binds again, or you could directly provide the instance. In this case, you’ll use the second option.
Open AppModule.kt and add the following definition to AppModule :
@Module
object AppModule {
// ...
@Provides
fun provideSequenceGenerator(): SequenceGenerator<Int> =
NaturalSequenceGenerator(0)
// ...
}
In this case, you just define provideSequenceGenerator() for binding NaturalSequenceGenerator type to the SequenceGenerator<Int> abstraction and, at the same time, create the instance. Don’t forget to annotate provideSequenceGenerator() with @Provides.
Handling the SequenceViewBinder.Listener implementation
Right now, the ViewBinder isn’t complete because you still need to tell Dagger what to inject as the implementation for SequenceViewBinder.Listener
As you saw earlier, this is the Presenter and it must be the same instance of the SequencePresenter implementation that you defined in the previous paragraphs. Because you’re binding a class to an abstraction, you can just add the following code to AppModule.kt:
@Module
object AppModule {
// ...
@Module
interface Bindings {
// ...
// 1
@Binds
// 2
fun bindViewBinderListener(impl: SequencePresenter):
// 3
SequenceViewBinder.Listener
}
}
With that code, you:
- Use
@Bindsto bind the implementation ofSequencePresenterto the one Dagger has to return as an object of typeSequenceViewBinder.Listener. - The parameter’s type is abstract. That’s possible because
SequencePresenterextendsSequenceViewBinder.Listener, as you saw in Figure 7.4. Again, the name of the function is only important for readability. - The return type is an abstraction:
SequenceViewBinder.Listener.
Whenever Dagger needs an instance of SequenceViewBinder.Listener, it will return the implementation for SequencePresenter according to what’s specified in the AppModule.
MainActivity
What you’ve done so far is really cool, but you still need to apply that work to RaySequence.
As you learned in the previous chapter, when you build the app, the annotation processor creates some code for you. So far, Dagger hasn’t done anything — it can’t until you create a @Component to use as the factory for the required instances. In RaySequence’s case, you need a Presenter and a ViewBinder. You’ll handle that next.
To start, open MainActivity.kt and change it like this:
class MainActivity : AppCompatActivity() {
// 1
lateinit var presenter: SequencePresenter
// 2
lateinit var viewBinder: SequenceViewBinder
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// 3
viewBinder.init(this)
}
// 4
override fun onStart() {
super.onStart()
presenter.bind(viewBinder)
}
// 5
override fun onStop() {
presenter.unbind()
super.onStop()
}
}
In this code, you:
- Define the
presenterproperty to be of typeSequencePresenter. - Define the
viewBinderproperty to have the typeSequenceViewBinder. - Invoke
init()on theviewBinderto initialize the UI into theonCreate()lifecycle method. - Bind the
viewBinderto thepresenterusingbind(), inherited fromBasePresenterin theonStart()lifecycle function. - Unbind the
viewBinderfrom thepresenterusing theunbind()operation inherited fromBasePresenterin theonStop()lifecycle function.
Build and run now… and it will crash. That’s because nobody initialized the lateinit vars. To do this, you need a @Component.
Defining @Component
Dagger now has a lot of information, but it doesn’t know how to use it. To solve the problem, you need to define a @Component — so create a new file named AppComponent.kt in the di package and copy the following code into it:
// 1
@Component(modules = [
AppModule::class,
AppModule.Bindings::class
])
interface AppComponent {
// 2
fun viewBinder(): SequenceViewBinder
// 3
fun presenter(): SequencePresenter
}
This code should already look familiar to you. It defines:
- A
@Componentto use as a factory for the instances of the dependency graph. You useAppModuleandAppModule.Bindingsto get the binding information. -
viewBinder()as a factory method to implementSequenceViewBinder. -
presenter()as a factory method forSequencePresenter.
Once again, the return type of the operation you define is what’s important for resolving the instances in the dependency graph.
Now, you can finally build the app and Dagger will generate some code for you. But how can you use that code in MainActivity? You’ll see next
Injecting into MainActivity
In the previous sections, you’ve configured the dependencies for the RaySequence app’s ViewBinder and Presenter. Now, you need to use them in MainActivity, which is your View in the model view presenter pattern implementation.
The simplest way of doing this is to open MainActivity.kt and change onCreate() to the following:
class MainActivity : AppCompatActivity() {
lateinit var presenter: SequencePresenter
lateinit var viewBinder: SequenceViewBinder
override fun onCreate(savedInstanceState: Bundle?) {
// 1
DaggerAppComponent.create().apply {
// 2
presenter = presenter()
viewBinder = viewBinder()
}
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
viewBinder.init(this)
}
// ...
}
In this code, you:
- Invoke the
create()static method onDaggerAppComponent, which Dagger generated for you, to create an instance of the@Componentyou defined using theAppComponentinterface. -
DaggerAppComponentis an implementation of theAppComponentinterface you created. It provides implementations for thepresenter()andviewBinder()operations, which you invoke to initializepresenterandviewBinder.
If you don’t like how the injection happens here, don’t worry, you’ll improve the code soon.
Now you can build and run, but the app still doesn’t work! It doesn’t crash, but when you click on the button, nothing happens. Everything should be fine, so what’s wrong?
As mentioned above, you didn’t use the best way to initialize presenter and viewBinder, but that’s not what’s causing the problem.
Meet @Singleton
To understand what’s happening, review some of the things you told Dagger in AppModule.kt. You told it to:
- Create an instance of
SequencePresenterImpl, invoking its primary constructor every time it needs an object of typeSequencePresenter. - Do the same every time it needs an instance of an implementation of
SequenceViewBinder.Listener.
However, this doesn’t answer a simple question: Is the instance it returns in those two cases the same instance? Or does Dagger create a new instance every time it needs a reference to an object?
To answer this question, you’ll make a simple change in the code to add some logs. Open SequenceViewBinderImpl.kt and add the following code to it:
class SequenceViewBinderImpl @Inject constructor(
private val sequenceViewListener: SequenceViewBinder.Listener
) : SequenceViewBinder {
init {
Log.d("DAGGER_LOG", "Listener: $sequenceViewListener") // HERE
}
// ...
}
With that code, you simply print the reference to the SequenceViewBinder.Listener implementation you’re using as listener of SequenceViewBinder.
Now, open MainActivity.kt and add the log:
class MainActivity : AppCompatActivity() {
// ...
override fun onCreate(savedInstanceState: Bundle?) {
DaggerAppComponent.create().apply {
presenter = presenter()
Log.d("DAGGER_LOG", "Presenter: $presenter") // HERE
viewBinder = viewBinder()
}
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
viewBinder.init(this)
}
// ...
}
This prints the reference to the instance of SequencePresenter that Dagger injects into MainActivity.
Now, build and run and use the filtering option in the LogCat window in Android Studio to see what’s happening, as in Figure 7.5:
Once you remove the irrelevant information, you get the log shown below. It proves that the instance of SequencePresenterImpl Dagger provides to MainActivity is not the same one it injects into SequenceViewBinderImpl to notify it about events on the Button. Of course, your values will differ from those shown below since each execution will give a different result.
D/DAGGER_LOG: Presenter: com...SequencePresenterImpl@211bb18
D/DAGGER_LOG: Listener: com...SequencePresenterImpl@51e2971
This means that, as things now stand, Dagger creates a new instance of a class every time it needs a reference to an object of the related type. In most cases you don’t need a new instance every time, you can use the same instance each time it’s injected.
Note: The ServiceLocator implementations you created in the first section of the book already took this into account. It didn’t create unnecessary instances.
How can you fix that? This is a good opportunity to introduce the @Singleton annotation.
Using @Singleton
You already met the fundamental concept of scope in Chapter 4, “Dependency Injection & Scopes”, and you’ll learn a lot more in the following chapters. However, it’s very important to say: @Singleton is nothing special.
Take careful note: Any annotation you use as a scope annotation is a way to bind the lifecycle of the objects handled by a specific @Component to the lifecycle of the @Component itself.
You’ll read the previous statement many times in the following chapters because it’s a fundamental concept you need to learn to master Dagger.
To see what this means, open SequencePresenterImpl.kt and replace the header of the class with this:
@Singleton // HERE
class SequencePresenterImpl @Inject constructor(
) : BasePresenter<MainActivity,
SequenceViewBinder>(),
SequencePresenter {
// ...
}
Using @Singleton, you tell Dagger that a @Component should only create a single instance of SequencePresenterImpl.
Build the app now, however, and Dagger will complain with the following error:
AppComponent.java:7: error: [Dagger/IncompatiblyScopedBindings]
com.raywenderlich.android.raysequence.di.AppComponent
(unscoped) may not reference scoped bindings:
public abstract interface AppComponent {
That’s because you didn’t tell Dagger everything! You told it to bind the instance of SequencePresenterImpl to the instance of a component, but you didn’t tell which @Component.
To tell Dagger that the scope for AppComponent is @Singleton, open AppComponent.kt and apply the following change:
@Component(modules = [
AppModule::class,
AppModule.Bindings::class
])
@Singleton // HERE
interface AppComponent {
fun viewBinder(): SequenceViewBinder
fun presenter(): SequencePresenter
}
By annotating AppComponent with @Singleton, you’re telling Dagger that the @Component is the factory for objects with or without a scope. If they have a scope, it must be the @Singleton scope.
For objects with no scope, like SequenceViewBinder, Dagger will create a new instance every time you need an object of that type. For objects annotated with @Singleton, AppComponent will always return the same instance.
Note: You’ll cover these concepts in detail in the following chapters. Remember that two different instances of
AppComponentwill always return different instances. Using@Singletonor another scope annotation won’t change this.
Build and run and check the log again. Now, you’ll see something like this:
D/DAGGER_LOG: Presenter: com...SequencePresenterImpl@211bb18
D/DAGGER_LOG: Listener: com...SequencePresenterImpl@211bb18
As you see, you always use the same SequencePresenterImpl now. More importantly, RaySequence works.
Using method injection
For the sake of completeness, take a quick look at how you’d achieve the same goal with method injection. The change is simple.
Open SequenceViewBinderImpl.kt and replace the first part of the code with the following:
class SequenceViewBinderImpl @Inject constructor(
) : SequenceViewBinder {
// 1
private var sequenceViewListener: SequenceViewBinder.Listener? = null
init {
Log.d("DAGGER_LOG", "Listener: $sequenceViewListener")
}
// 2
@Inject
fun configSequenceViewListener(listener: SequenceViewBinder.Listener) {
sequenceViewListener = listener
}
// ...
}
Now:
-
sequenceViewListeneris not a primary constructor parameter, but rather a private property of optional typeSequenceViewBinder.Listener?. - You define
configSequenceViewListener()with a single parameter of typeSequenceViewBinder.Listenerand you annotate it with@Inject.
Build and run and you’ll note that the app still works. You already learned when to use method injection and what the possible advantages are in Chapter 3, “Dependency Injection”. However, it’s useful to know that Dagger supports it, as well.
Cleaning up the injection for MainActivity
Earlier, you read that you’d use a better way to inject the Presenter and ViewBinder into the MainActivity. Good news — you’re ready to do that now.
When you have a deeper understanding of how @Components work, you’ll discover different approaches for the injection. But after reading this chapter, you already have all the information to implement something similar to the Injector<T> you saw in Chapter 4, “Dependency Injection & Scopes”. You’ll do that next.
Open AppComponent.kt and replace its contents with the following:
@Component(modules = [
AppModule::class,
AppModule.Bindings::class
])
@Singleton
interface AppComponent {
// HERE
fun inject(mainActivity: MainActivity)
}
With this code, you replaced the factory method for SequenceViewBinder and SequencePresenter with a function that accepts MainActivity as the single parameter.
Note: Using the name
inject()for these functions is a convention. However, nothing’s stopping you from picking another name, if you prefer.
Now, you need to utilize the code Dagger generates. Open MainActivity.kt and replace the first part of the class with the following:
class MainActivity : AppCompatActivity() {
// 1
@Inject
lateinit var presenter: SequencePresenter
// 2
@Inject
lateinit var viewBinder: SequenceViewBinder
override fun onCreate(savedInstanceState: Bundle?) {
// 3
DaggerAppComponent.create().inject(this)
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
viewBinder.init(this)
}
// ...
}
Here, you:
- Annotate the
presenterwith@Inject. - Do the same for
viewBinder. - Create the
DaggerAppComponentinstance usingcreate()and invokeinject()on it, passing the reference to theMainActivityitself.
In this case, it’s important to note that what matters here is the type of the parameter of the inject() operation. This cannot be a generic type, but must be the explicit type of the object destination of the injection.
Build and run. Now, everything works as expected. You’ve seen that Dagger allows you to define, declaratively, how to generate an Injector for a given class. Cool, right? :]
Key points
- Dagger supports constructor, method and field injection.
-
@Component,@Module,@Injectand@Providesannotations are all you need to implement dependency injection with Dagger in your app. -
@Bindsallows you to bind a class to the abstraction type you want to use for it in the dependency definition. - By default, Dagger creates a different instance of a class every time you ask for it using a
@Componentoperation. -
@Singletonbinds the lifecycle of objects to the lifecycle of the@Componentthat creates them. - Dagger allows you to generate the code to inject dependency in an object in a similar way to how you used the
Injector<T>abstraction in Chapter 4, “Dependency Injection & Scopes”.
Where to go from here?
Congratulations! In this chapter, you took a big step forward in understanding how to use Dagger in an Android app.
Using the RaySequence app, you learned how to use the main Dagger annotations in a simple model view presenter architecture. You learned how the @Module, @Component, @Provides and @Inject annotations help you define the dependency graph. Finally, you met the @Binds and @Singleton annotations for the first time and used them to solve some very common problems.
There are many other scenarios that Dagger can help with, however. In the next chapter, you’ll learn everything you need to know about the @Module annotation.