Dagger 2 Tutorial For Android: Advanced
In this tutorial, you’ll learn about the advanced concepts of Dagger. You’ll learn about component lifecycles, @Binds, and component builders and factories. 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
35 mins
- Getting Started
- Taking a Closer Look
- How Can Dagger Help?
- Understanding the Dependency Graph
- What Object To Inject
- Defining Inject Targets
- Scope Management
- Providing Singleton Values
- Managing the @Component Lifecycle
- Sharing the @Component
- Using @Binds
- Binding Dependencies
- Improving Binds
- Hidden Power of @Binds
- The @Component.Builder Interface
- Providing Parameterized Dependencies
- Improving Parameterized Dependencies
- Making Dagger Work for You Again
- Using the @Component.Factory Interface
- Where to Go From Here
Hidden Power of @Binds
The true power of both @Binds and static @Provides is that Dagger doesn’t generate factory classes which wrap those functions. This is the part about generating less code and increasing performance. Not only does it increase performance at build-time, but it also increases the runtime performance because Dagger is no longer allocating extra classes for each component you create.
Finally, by using @Inject and @Binds, you’ve abstracted away the creation of the repository dependency. This means that if you change the constructor parameters in the MemoryNewsRepository, you won’t have to change the provider function. It’ll update automatically, because of the @Inject.
If you did this for all the dependencies, you could freely update the constructors by adding or removing parameters, changing their order, and you wouldn’t have to do the extra work of updating the provider/factory functions.
The @Component.Builder Interface
Look at the last implementation of the AppModule. You can see that you needed to do magic with the companion object and static methods because of the use of @Binds which required the annotated method to be abstract.
But what if you want to provide the instance for the NewsRepository type when you instantiate the AppComponent? To do this, change the implementation of the AppModule class like this:
@Module
class AppModule(
// 1
private val newsRepository: NewsRepository
) {
// 2
@Provides
fun provideNewsListPresenter(): NewsListPresenter = NewsListPresenterImpl(newsRepository)
// 2
@Provides
fun provideNewsDetailPresenter(): NewsDetailPresenter = NewsDetailPresenterImpl(newsRepository)
}
See the following new steps:
- The
AppModulenow defines a constructor with a parameter of the typeNewsRepository. - The same is then used to create the instances that
@Providesannotated methods return. The methods don’t have any parameters now.
If you build the application now, you’ll get an error in InitApp, saying DaggerAppComponent.create() doesn’t exist.
Providing Parameterized Dependencies
If the @Module used by a @Component needs a parameter, Dagger doesn’t generate create(). It instead generates an implementation of the Builder pattern which requires you to provide the parameterized dependencies. Change the code in the InitApp like this:
class InitApp : Application() {
lateinit var appComponent: AppComponent
override fun onCreate() {
super.onCreate()
appComponent = DaggerAppComponent
.builder() // 1
.appModule(AppModule(MemoryNewsRepository())) // 2
.build() // 3
}
fun appComp() = appComponent
}
As you can see:
- The
DaggerAppComponentthat Dagger provides now has abuilder()which returns an implementation of aComponent.Builder. - You create an instance of the
MemoryNewsRepositoryyou need, to create the instance of theAppModule. You then pass to theAppComponentbuilder the createdAppModuleusingappModule(). - The last method to invoke in the Builder implementation is called
build>(). It returns theAppComponentimplementation.
In this case, you don’t need the @Singleton annotation anymore. This is because you’re responsible of the creation of the MemoryNewsRepository class which is then used by the AppComponent any time it needs it.
Remove @Singleton from the MemoryNewsRepository and AppComponent as in these definitions:
class MemoryNewsRepository : NewsRepository {
- - -
}
@Component(modules = [AppModule::class])
interface AppComponent {
- - -
}
Build and run the app. It now works as expected, reusing the same instance of MemoryNewsRepository in all the fragments.
RwNews App

RwNews App
On one hand, you own the MemoryNewsRepository, so passing it in as a parameter didn’t do much. On the other hand, this is how you pass the reference to the Context, the Resources, or any other runtime-created dependency you don’t own in Andriod.
Improving Parameterized Dependencies
In the previous code, you created the MemoryNewsRepository in the InitApp and passed it to the builder of the AppComponent encapsulating it within an instance of the AppModule.
You can do better and pass only what Dagger really needs: The instance of the MemoryNewsRepository. To do this, change the current code in the AppComponent.kt to get the following implementation:
@Component(modules = [AppModule::class])
interface AppComponent {
fun inject(frag: NewsListFragment)
fun inject(frag: NewsDetailFragment)
// 1
@Component.Builder
interface Builder {
// 2
@BindsInstance
fun repository(repo: NewsRepository): Builder
// 3
fun build(): AppComponent
}
}
There are many important things to note here:
- You created an inner interface annotated with
@Component.Builder. The name is usuallyBuilderbut it’s not important. - The interface needs to define some operations similar to the ones you created for
@Binds. Each of them must have a single parameter of the type of the object you need to inject and must have the same interface as return type.
This allows for chaining of such methods. Each of these methods must be annotated with@BindsInstance. - Finally, need to define a method whose name must be
buildwhich must have the component as the return type. In this case, the type isAppComponent.
As you can see, there are many musts you need to follow.
Now you open the AppModule and remove the parameter from the constructor getting the following implementation:
@Module
class AppModule {
@Provides
fun provideNewsListPresenter(newsRepository: NewsRepository): NewsListPresenter =
NewsListPresenterImpl(newsRepository)
@Provides
fun provideNewsDetailPresenter(newsRepository: NewsRepository): NewsDetailPresenter =
NewsDetailPresenterImpl(newsRepository)
}
You kinda went all the way back to square one. Such a déjà vu. :]
To build and run the app, you need to change the way you create the AppComponent within InitApp like this:
class InitApp : Application() {
- - -
override fun onCreate() {
super.onCreate()
appComponent = DaggerAppComponent
.builder()
.repository(MemoryNewsRepository()) // HERE
.build()
}
- - -
}
Dagger created the repository() for the Builder which lets you pass the MemoryNewsRepository directly. Now build and run the project and the app still runs successfully! :]
RwNews App

RwNews App
Making Dagger Work for You Again
The previous implementation for the AppModule was a déjà vu because it’s very similar to one of the first implementations. But now you also know how to use @Binds! Change the AppModule class to this to re-implement @Binds:
@Module
abstract class AppModule {
@Binds
abstract fun provideNewsListPresenter(newsRepository: NewsListPresenterImpl): NewsListPresenter
@Binds
abstract fun provideNewsDetailPresenter(newsRepository: NewsDetailPresenterImpl): NewsDetailPresenter
}
This isn’t enough. While Dagger knows what classes to use as implementation of the NewsListPresenter and NewsDetailPresenter, it doesn’t know how to create an instance of NewsListPresenterImpl and NewsDetailPresenterImpl.
You already know the solution: @Inject. Add the annotation to the constructors like in the following code:
class NewsDetailPresenterImpl @Inject constructor(
private val newsRepository: NewsRepository
) : BasePresenter<NewsModel, NewsDetailView>(),
NewsDetailPresenter {
- - -
}
and
class NewsListPresenterImpl @Inject constructor(
private val newsRepository: NewsRepository
) : BasePresenter<NewsListModel, NewsListView>(),
NewsListPresenter {
- - -
}
Build and run the app as usual.
RwNews App

RwNews App