Dagger 2 Tutorial for Android: Advanced – Part 2
In this tutorial, you’ll learn how to implement advanced features of Dagger 2 by using subcomponents, custom scopes and multibinding. By Massimo Carli.
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Contents
Dagger 2 Tutorial for Android: Advanced – Part 2
30 mins
- Getting Started
- Managing Multiple @Scopes
- Exploring @Singleton
- Using @Singleton
- Creating a Feature @Component
- Managing @Component Dependencies
- Creating Your Custom @Scope
- Dependencies Between Differently-Scoped @Components
- Injecting With a Custom @Component
- Connecting the Custom @Scope
- Component Dependencies With @Subcomponent
- Migrating to @Subcomponent
- @Component Dependencies Versus @Subcomponent
- Multibinding in Dagger
- Adding Different Implementations
- Implementing Multibinding
- Getting Dagger to Use the Multibinding Implementation
- Where to Go From Here?
As you saw in part one of our Dagger 2 Tutorial For Android: Advanced, Dagger is one of the most-used dependency injection (DI) libraries in Android. It helps you generate code to provide the dependencies for your app. By using it correctly and combining it with your architecture setup, you make your dependency injection clear and distinct – without a lot of work! :]
In the previous tutorial, you tried the main concepts and use cases of Dagger with @Inject
, @Module
and @Component
. You used @Binds
and experimented with methods of creating @Component
instances using @Component.Builder
and @Component.Factory
.
Finally, you learned about @Singleton
and its relationship with @Component
s that use it.
As your app grows in size and complexity, you might add multiple @Component
s. You might also need to separate code into smaller logical groups to reuse across the project by creating different Android Studio modules.
For these cases, Dagger offers powerful tools like @Subcomponent
and an interesting feature called multibindings, which allows you to bind the same type, in a map or set of dependencies of that type. This way you can pick and choose which concrete implementation you need. On top of that, performance is always important, so it’s helpful to use different @Scope
s.
In this tutorial you’ll:
- Use
@Singleton
to define a custom@Scope
. - Learn how to bind custom
@Scope
s to dependent@Component
instances. - Manage dependencies between different
@Component
s through theirdependencies
attribute. - Learn what
@Subcomponent
a are and how they manage dependencies between@Component
s with different@Scope
s. - Find out what multibinding is and how you can use it in your apps.
It also presumes you have knowledge of Dagger 2. If not, check out our Dependency Injection in Android with Dagger 2 and Kotlin and Dagger 2 Tutorial For Android: Advanced – Part One tutorials.
It also presumes you have knowledge of Dagger 2. If not, check out our Dependency Injection in Android with Dagger 2 and Kotlin and Dagger 2 Tutorial For Android: Advanced – Part One tutorials.
Getting Started
Download and unzip the materials for this tutorial using the Download Materials button at the top or bottom of this page. Open the project using Android Studio 3.5 or greater, then build and run it. You’ll see this:
In this tutorial, you’ll work on RwNews, a basic app that displays a list of news items. The user can select any news item to read its content.
The app is very simple, but its functionality is not as important as its internal workings.
Look at the project structure in Android Studio. You’ll notice a classic MVP architectural pattern implementation with definitions as in the following UML class diagram:
This is the same app from the previous Dagger 2 Tutorial For Android: Advanced tutorial.
Open InitApp.kt and you’ll see the following:
class InitApp : Application() {
lateinit var appComponent: AppComponent
override fun onCreate() {
super.onCreate()
appComponent = DaggerAppComponent
.factory()
.repository(MemoryNewsRepository()) // HERE
}
fun appComp() = appComponent
}
Here, you explicitly create a single instance of MemoryNewsRepository
. You pass its reference to AppComponent
, then share a single instance between all the different Fragment
implementations.
But Dagger allows you to get the same result in a better way, without hard-coding dependency creation.
Managing Multiple @Scopes
Dagger doesn’t just generate code, it also allows you to manage dependencies and their lifecycle within the components you define. To do that, it needs to understand how different objects connect and how to provide a way to build the related graph at runtime.
Creating the MemoryNewsRepository
instance should be Dagger’s responsibility, as well as creating NewsListPresenterImpl
and NewsDetailPresenterImpl
.
All objects need memory when you create them, but you don’t always need to create all of the objects at the same time. Your app always needs an instance of MemoryNewsRepository
, but you only need an instance of NewsDetailPresenterImpl
when you open NewsDetailFragment
.
As such, different objects have different lifecycles. This is what @Scope
represents.
Exploring @Singleton
Look at the source code for @Singleton
:
// 1
package javax.inject;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
@Scope // 2
@Documented // 3
@Retention(RUNTIME) // 4
public @interface Singleton {} // 5
Note that:
- It’s not a Dagger definition. The package
javax.inject
comes from a the JSR-330 which is the Java Specification Request about annotations. -
@Scope
allows you to tag this definition as a scope.@Scope
is an example of a tagging annotation and is part of the samejavax.inject
package. - Annotations are usually not included in JavaDocs.
@Documented
allows you to inform tools that this should be part of the JavaDoc. -
@Retention
is very important. It informs the compiler of the level of retention or persistence of the annotation during the building process.RUNTIME
indicates that the annotation persists up to the execution of the code you annotated. - The
@Singleton
doesn’t have any attributes, which is common for most scopes.
As you can see in the previous definition, the @Singleton
is just a scope-tagged annotation. It’s used to tag other annotations or classes, to add extra scoping behavior.
Using @Singleton
The previous tutorial demonstrated, that @Singleton
‘s power transfers to the @Component
that uses it. When you define a @Component
with a lifecycle of the Application
object and annotate it with @Singleton
, all the objects within the same dependency graph with that annotation will have the same lifecycle.
To make your app rely on @Singleton
, open AppComponent.kt and remove @Component.Factory
. Then add @Singleton
, ending up with the following implementation:
@Component(modules = [AppModule::class])
@Singleton // HERE
interface AppComponent {
fun inject(frag: NewsListFragment)
fun inject(frag: NewsDetailFragment)
}
Now, change the content of the MemoryNewsRepository.kt like so:
@Singleton // HERE
class MemoryNewsRepository @Inject constructor(): NewsRepository {
- - -
}
Here, you bind the lifecycle of MemoryNewsRepository
to the @Component
that manages it, using @Singleton
.
Then, add the following definition at the end of the AppModule.kt:
@Module
abstract class AppModule {
- - -
@Binds
abstract fun provideNewsRepository(newsRepository: MemoryNewsRepository): NewsRepository
}
This tells Dagger what implementation of the <code>NewsRepository</code> interface to use.
Finally, insert the Dagger-generated create()
into AppInit
like this:
class InitApp : Application() {
lateinit var appComponent: AppComponent
override fun onCreate() {
super.onCreate()
appComponent = DaggerAppComponent.create() // HERE
}
fun appComp() = appComponent
}
At this point, Dagger has all the information it needs to create an instance of MemoryNewsRepository
. It can use it in every place you need a NewsRepository
.
Build and run the app!
Verify you’re using the same instance of MemoryNewsRepository
by using the string AdvDagger as a filter in LogCat and checking the output:
I/AdvDagger: In NewsListPresenterImpl using Repository .repository.impl.MemoryNewsRepository@e2aaaab
I/AdvDagger: In NewsDetailPresenterImpl using Repository .repository.impl.MemoryNewsRepository@e2aaaab
I/AdvDagger: In NewsListPresenterImpl using Repository .repository.impl.MemoryNewsRepository@e2aaaab
I/AdvDagger: In NewsDetailPresenterImpl using Repository .repository.impl.MemoryNewsRepository@e2aaaab
Using @Singleton
both on the component and the dependency, you’ll reuse the same instance of the dependency as long as the component instance is alive. As long as you initialize the component only once, this will be true, and you’re doing that in InitApp
. :]