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
Managing the @Component Lifecycle
What does it mean to bind the lifecycle of an object to one of the @Components that manages it? It means that if you want a single instance of the MemoryNewsRepository, you need to have a single instance of the AppComponent, reusing it throughout your app.
The Android solution is to create a custom implementation of the Application and store the component within.
Create a new class named InitApp in rwnews and add the following code:
class InitApp : Application() {
// 1
private lateinit var appComponent: AppComponent
override fun onCreate() {
super.onCreate()
// 2
appComponent = DaggerAppComponent.create()
}
// 3
fun appComp() = appComponent
}
This class which extends the Application. In the code above, it does the following:
- Defines the
appComponent, containing a reference to the instance which Dagger creates. - Creates the instance of the
AppComponentusingcreate()onDaggerAppComponent. Remember that theDaggerAppComponentis an implementation generated by Dagger once you build your project. - Defines
appComp()to expose theappComponent.
Next, you need to tell Android to use this definition into the app. Open AndroidManifest.xml and set the .InitApp as the value for the android:name attribute of the application element.
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.raywenderlich.rwnews">
<application
- - -
android:name=".init.InitApp"> // HERE
</application>
- - -
</manifest>
When you launch the app, Android creates a new instance of the custom Application implementation and invokes onCreate() on it. This creates an instance of the AppComponent and stores it within InitApp.
Now you can proceed to use and share the instance of the component in your target inject classes.
Sharing the @Component
Next, you need to access the same instance of the AppComponent in every place you need it.
Open NewsListFragment.kt and replace the code from onAttach() with the following:
class NewsListFragment : Fragment(), NewsListView {
@Inject
lateinit var newsListPresenter: NewsListPresenter
- - -
override fun onAttach(context: Context) {
(context.applicationContext as InitApp) // HERE
.appComp().inject(this)
super.onAttach(context)
}
- - -
}
Here you’re getting the applicationContext and casting it to InitApp to call appComp() which returns the reference to the single AppComponent within InitApp.
Do the same in NewsDetailFragment.kt:
class NewsDetailFragment : Fragment(), NewsDetailView {
- - -
override fun onAttach(context: Context) {
(context.applicationContext as InitApp)
.appComp().inject(this)
super.onAttach(context)
}
- - -
}
Now build and run the app and open a news item once or twice. Check the log in output. It should be similar to the following:
I/AdvDagger: In NewsListPresenterImpl using Repository MemoryNewsRepository@82183c0
I/AdvDagger: In NewsDetailPresenterImpl using Repository MemoryNewsRepository@82183c0
I/AdvDagger: In NewsListPresenterImpl using Repository MemoryNewsRepository@82183c0
I/AdvDagger: In NewsDetailPresenterImpl using Repository MemoryNewsRepository@82183c0
The instance of the NewsRepository is always the same. Remember, this isn’t happening because of the @Singleton annotation, but because the instance of the AppComponent you’re using for injection is the same and precisely the one you stored into the InitApp class.
Using @Binds
The current app works, but you can apply optimizations to reduce the code generation time, as well as the quantity of the generated code. You can rely on @Binds instead of @Provides to do this.
Open the AppModule.kt and look at the following definition:
@Module
class AppModule {
- - -
@Provides
@Singleton
fun provideRepository(): NewsRepository = MemoryNewsRepository()
}
provideRepository() informs Dagger that the implementation to use for the NewsRepository is MemoryNewsRepository. You’re doing some work here because you’re the one that’s creating the instance, and defining the provider contract.
You can avoid this, and delegate the creation of the implementation to Dagger, by using @Binds. It lets you bind a specific interface to the class constructor for the implementation, hence the name.
Delete provideRepository() from the AppModule and create a new file named NewsRepositoryModule.kt within the di package. Then add this code to it:
@Module
abstract class NewsRepositoryModule {
@Binds
abstract fun provideRepository(repoImpl: MemoryNewsRepository): NewsRepository
}
This is a new @Module that tells Dagger the class bound to the NewsRepository is MemoryNewsRepository. You do this by defining an abstract method which accepts a single parameter of the implementation type and has the interface type as return type. Because the method is abstract the class is also abstract.
In Kotlin, you’d have to use the @JvmStatic annotation for provide methods into a companion object. You’ll learn how to do this later.
AppModule class in place of the one above. In that case, the class must be abstract, and Dagger forces you to make the other provide methods static.
In Kotlin, you’d have to use the @JvmStatic annotation for provide methods into a companion object. You’ll learn how to do this later.
Next, open the AppComponent and add the module to modules in @Component. Its definition becomes the following:
@Component(modules = [AppModule::class, NewsRepositoryModule::class])
@Singleton
interface AppComponent {
- - -
}
There’s one last step to finish binding the interface to the implementation.
Binding Dependencies
As the last step, you need to remember what you read at the beginning of this tutorial. Dagger needs to know how to create instances.
If you build the app at this point, Dagger will complain that it can’t create an instance of the MemoryNewsRepository because it doesn’t know how to yet. You still need to annotate the MemoryNewsRepository‘ constructor with @Inject. So, open it and add change the class signature to this:
@Singleton
class MemoryNewsRepository @Inject constructor() : NewsRepository {
- - -
}
You also need to use @Singleton because it’s a property of the specific implementation and not of the NewsRepository abstraction. For this reason, using @Singleton with @Binds is sometimes considered bad practice.
Now build and run the app and check that it works as expected!
RwNews App

RwNews App
Improving Binds
Dagger introduced @Binds as a more compact way of declaring the relation between an abstraction type and its implementation. It lets you delegate the creation of the actual implementation to Dagger as you did with the MemoryNewsRepository. More importantly, it reduces the code generation time along with the number of lines of generated code.
You might argue that you had to create a new class for this as a consequence of the problem of having @Provides methods within an abstract class. This way it seems like you’re adding extra code, to reduce code, which doesn’t make much sense, right? Well, having to declare a small module was simple, and the performance gains behind are worth it, once you start adding more and more dependencies in your project.
But if you still want to avoid having to create a module for @Bind annotated providers, then you can use a different approach, as noted below:
@Module
// 1
abstract class AppModule {
// 2
@Module
companion object {
// 3
@JvmStatic
@Provides
fun provideNewsListPresenter(repo: NewsRepository): NewsListPresenter =
NewsListPresenterImpl(repo)
// 3
@JvmStatic
@Provides
fun provideNewsDetailPresenter(repo: NewsRepository): NewsDetailPresenter =
NewsDetailPresenterImpl(repo)
}
// 4
@Binds
abstract fun provideRepository(repoImpl: MemoryNewsRepository): NewsRepository
}
Now the AppModule is an abstract class and you had make some relatively big changes:
- The
AppModuleis nowabstract. - You need a
companion objectfor the definitions of@Provides. It’s also important to annotate this object with@Moduleas well. - Providers also annotated with
@JvmStaticwhich is a Kotlin annotation useful if you want to make this functionstatic, in the meaning of Java, in the companion object. - Now you can use
@Bindshere.
Before building you need to clean what you did previously. Delete NewsRepositoryModule.kt and remove NewsRepositoryModule::class from the AppComponent.
Now, build and run. Check that everything is working as expected.
RwNews App

RwNews App