Home · Android & Kotlin Tutorials

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.

4.8/5 4 Ratings

Version

  • Kotlin 1.3, Android 4.1, Android Studio 3.5

Dagger is arguably the most used Dependency Injection, or DI, framework for Android. Many Android projects use Dagger to simplify building and providing dependencies across the app. It gives you the ability to create specific scopes, modules, and components, where each forms a piece of a puzzle: The dependency graph.

Unfortunately, Dagger can’t solve every problem, and it’s not easy to learn. It has many features you can use in many ways depending on the use case.

The trick here is to think of Dagger not as a DI framework but as a code generator tool that needs to know how to create instances of certain classes. Then you, as a developer, tell Dagger how to build and connect them.

As you may know, Dagger revolves around the use of annotations, which help it read and generate all the code it needs to work. All the different annotations are simply a way of telling Dagger what it doesn’t know to achieve its goal and create the code you otherwise would have to write yourself.

In this tutorial, you’ll learn:

  • How to use @Singleton and what it really means.
  • The concept of the lifecycle of a @Component.
  • When and how to use @Binds.
  • When to use @Component.Builder and @Component.Factory.
Note: This tutorial assumes that you’re already familiar with the basics of Android development and Android Studio. If Android development is new to you, first read through the Beginning Android Development and Kotlin for Android tutorials. It also presumes you have some knowledge of Dagger 2, so if you’re not familiar, you can check out our Dependency Injection in Android with Dagger 2 and Kotlin tutorial, which covers some of the same topics, but at a less detailed level.

Getting Started

Download and unzip the materials by clicking the Download Materials button at the top or bottom of this tutorial. Open the project using Android Studio 3.5 or greater and then run it. You should see something like this:

User selects a generic title from a list and the story displays

RwNews App

As you can see, it’s a basic app that displays a list of news items, allows the user to select one and displays its content. However, in this context, the app’s functionality isn’t as important as the internal works! This is why when building and running the app, you may not see visual changes, but the internal structure and performance will indeed be different! :]

Taking a Closer Look

If you look at the project structure in Android Studio, you’ll notice a classic MVP architectural pattern implementation with the definitions as in the following UML class diagram:

Diagram shows the MVP architecture of app with connections between components

RwNews Architecture

The diagram only displays the classes that are useful in terms of dependencies. It’s important to understand what kind of dependencies Dagger can manage. In the following diagram, you’ll see which dependencies you’ll provide through Dagger:

Arrows from NewsDetailFragment and NewsDetailPresenterlmpl to interface NewsDetailPresenter.

Main Dependencies

The dotted line with the open arrow represents the dependency between two definitions. The previous diagram shows that NewsDetailFragment depends on the NewsDetailPresenter. You can also say that NewsDetailFragment uses the NewsDetailPresenter.
Interface inheritance is a special type of dependency. It describes the relation between a class and one of the interfaces it implements.

Note:It’s important to remember that A depends on B if any changes in B can introduce changes in A.

In the previous diagram, you can see that the NewsDetailPresenterImpl is an implementation, or realization, of the NewsDetailPresenter. Futhermore, the same type of a relation occurs between the NewsListPresenterImpl and the NewsListPresenter.

Using interfaces can help you achieve abstraction since you can depend on the interface, rather than the concrete implementation. This allows you to change the implementation completely, while the external API stays the same.

You can also see that the NewsListFragment and NewsDetailFragment don’t depend directly on the NewsRepository but through a specific presenter implementation. With that in mind, the MemoryNewsRepository is the only concrete implementation of NewsRepository, so you’ll use it in both of the presenter implementations: NewsListPresenterImpl and NewsDetailPresenterImpl.

Finally, you can see that the full app depends on the common module containing some general-purpose classes and interfaces.

How Can Dagger Help?

This is a small project, but even now, organizing dependencies in a performant way might be difficult. At this point, it’s important to repeat what Dagger is: A tool for generating code to manage dependencies between different objects. To do this it needs to:

  1. Understand the dependency graph.
  2. Create instances of the dependencies and the end objects to inject.
  3. Execute the injection.

Understanding the Dependency Graph

The first step of setting up the Dependency Graph is to read out the structure of all the classes and dependencies you need to provide and use. Dagger does this first step using reflection during the build phase of the project. For instance, look at the signature of the constructor of the NewsDetailPresenterImpl:

class NewsDetailPresenterImpl(
  private val newsRepository: NewsRepository // HERE
) : BasePresenter<NewsModel, NewsDetailView>(),
  NewsDetailPresenter {
  - - - 
}

You can see that the NewsDetailPresenterImpl needs a reference to an object of type NewsRepository. This means that to create an instance of NewsDetailPresenterImpl, you need an object of type NewsRepository: The former type depends on the latter. You’ll repeat the process for all classes and tell Dagger how to create a graph of dependencies.

What Object To Inject

To create an instance of the NewsDetailPresenterImpl class, Dagger needs to know how to create an object of the NewsRepository type. But it requires more information from you because this is an interface and you can’t simply instantiate it and use it. To accomplish the second task, you need to provide a @Module whose responsibility is to give Dagger the information about the specific class to instantiate for a specific type:

@Module
class AppModule {

  // 1
  @Provides
  fun provideNewsListPresenter(repo: NewsRepository): NewsListPresenter =
    NewsListPresenterImpl(repo)

  // 2
  @Provides
  fun provideNewsDetailPresenter(repo: NewsRepository): NewsDetailPresenter =
    NewsDetailPresenterImpl(repo)

  // 3
  @Provides
  fun provideRepository(): NewsRepository = MemoryNewsRepository()
}

With the previous code, you tell Dagger that:

  1. It should use a NewsListPresenterImpl when injecting a NewsListPresenter instance.
  2. The implementation for the NewsDetailPresenter is a NewsDetailPresenterImpl instance.
  3. You want to use a MemoryNewsRepository as an implementation of the NewsRepository.

You’re actually doing more: You’re creating the instances of the required classes as return values of methods annotated with @Provides. What Dagger does for you is managing the parameter of type NewsRepository passing the appropriate reference in the component graph creation phase.

Defining Inject Targets

Now Dagger knows how to create the dependency graph, but it doesn’t know what to do with it. To command Dagger to use the dependencies, you have to define a @Component. Its responsibility is to expose objects you want, from the dependency graph, and to inject those dependencies to target classes.

Dagger supports all three types of injection: Constructor, field and method injection.

You should use constructor injection the most because it allows you to set all the dependencies for an object when you create it. This is the type of injection you used for the NewsDetailPresenterImpl class.

Sometimes this isn’t possible because you don’t have direct control over the creation of the instance of a class. This is the case of classes like Activity and Fragment whose lifecycle is the responsibility of the Android environment. In this case, you use the field injection, which injects dependencies into a class field, like so:

class NewsListFragment : Fragment(), NewsListView {

  // 1
  @Inject
  lateinit var newsListPresenter: NewsListPresenter

  private lateinit var recyclerView: RecyclerView
  private lateinit var newsListViewAdapter: NewsListViewAdapter
  private val newsListModel = NewsListModel(emptyList())
  - - -
}

However, Dagger won’t inject the presenter, even though you’ve annotated it. It forces you to define a @Component and inject to the target class manually. So to continue the setup, you need to add both the module and inject functions to the component, like so:

@Component(modules = [AppModule::class]) // 1
interface AppComponent {

  fun inject(frag: NewsListFragment) // 2

  fun inject(frag: NewsDetailFragment)
}

There are two important things to note here:

  1. Each component can only provide dependencies from the modules assigned to it.
  2. You need to declare each target injection class as a function with the parameter of the type of that class. In this case, the inject() functions represent this, but you can essentially name them whatever you want. This is just the convention.

You can also include modules within other modules, like a composition of functions. Then, once you add one module to a component, you add all those included in that composition. The syntax for doing so would be the following:

class SomeOtherModule

@Module(includes = [SomeOtherModule::class])
class AppModule

Because your project and setup are fairly simple, you don’t need multiple modules, to separate dependencies by their layers or types, however, it’s useful to know how to apply the separation when you encounter more complex projects! Move on to the last step of the setup! :]

Finally, you have to add the following code in one of the lifecycle methods of the target injection classes. For example, you can do so in onAttach(), in fragments:

override fun onAttach(context: Context) {
  // HERE
  DaggerAppComponent.create().inject(this)
  super.onAttach(context)
}

You call inject() on the instance of the AppComponent implementation which Dagger creates for you.

After calling inject(), Dagger guarantees to inject the proper reference for all the fields with @Inject, if they exist in the dependency graph. If they’re missing from the graph, you’ll get a compile-time error telling you which dependency is missing and where.

Scope Management

Open MemoryNewsRepository, which is an implementation of NewsRepository. You’re creating the data for the app within init(). Init should run once per class initialization.

But when, and how many times, does that initialization really happen? Because this is a repository, and you’re using it to fetch data from some entity, it typically should be unique in the app.

Check if the repository is indeed initialized once. Open the NewsListPresenterImpl. There you’ll see a log message as follows:

class NewsListPresenterImpl(
  private val newsRepository: NewsRepository
) : BasePresenter<NewsListModel, NewsListView>(),
  NewsListPresenter {

  override fun displayNewsList() {
    Log.i(TAG, "In NewsListPresenterImpl using Repository $newsRepository") // LOG
    val newsList = newsRepository.list()
    view?.displayNews(NewsListModel(newsList))
  }
}

Run the app, select a news item from the list, go back and then select another news item. Open Logcat and filter the logs using AdvDagger as the tag. Notice a log similar to the following, although you’ve simplified it by removing the full package and time information:

I/AdvDagger: In NewsListPresenterImpl using Repository MemoryNewsRepository@82183c0 // SAME
I/AdvDagger: In NewsDetailPresenterImpl using Repository MemoryNewsRepository@464edaa // DIFFERENT
I/AdvDagger: In NewsListPresenterImpl using Repository MemoryNewsRepository@82183c0 // SAME
I/AdvDagger: In NewsDetailPresenterImpl using Repository MemoryNewsRepository@99c4f98 // DIFFERENT

As you can see, every time Dagger injects a different instance of the NewsRepository. Because of the implementation of the MemoryNewsRepository, this means the initialization happens every time Dagger injects the NewsRepository.

This is something that shouldn’t happen. It adds more overhead to your app’s memory and hinders the performance. You can improve that by making the repository unique.

Providing Singleton Values

You can bind a specific dependency to the lifetime of a component by using a @Scope. The first scope you usually meet when learning about Dagger is @Singleton.

Open the AppModule and update it by adding @Singleton to the the MemoryNewsRepository provider as follows:


// Other imports
import javax.inject.Singleton  // HERE

@Module
class AppModule {

  - - -

  @Provides
  @Singleton // HERE
  fun provideRepository(): NewsRepository = MemoryNewsRepository()
}

You’ll also need to import the related package at the beginning of the class. If you try building the app you’ll get the following error:

e: ...AppComponent.java:7: error: [Dagger/IncompatiblyScopedBindings] com.raywenderlich.rwnews.di.AppComponent (unscoped) may not reference scoped bindings:

This happens because you define the NewsRepository implementation in the AppModule class, which contains information that the AppComponent uses to create the instances of its dependency graphs. If you annotate its @Provides method with @Singleton you assign it a scope that the @Component must know to understand when to create its instance.

Head over to the AppComponent and add @Singleton under @Component:

@Component(modules = [AppModule::class])
@Singleton // HERE
interface AppComponent {

  - - -
}

This scopes the repository to the lifetime of AppComponent instances. Build and run, and once again open Logcat. The output is the same as the one before, where each instance has a different @xxxxxx hashcode.

I/AdvDagger: In NewsListPresenterImpl using Repository MemoryNewsRepository@82183c0 // SAME
I/AdvDagger: In NewsDetailPresenterImpl using Repository MemoryNewsRepository@adf2d9b // DIFFERENT
I/AdvDagger: In NewsListPresenterImpl using Repository MemoryNewsRepository@82183c0 // SAME
I/AdvDagger: In NewsDetailPresenterImpl using Repository MemoryNewsRepository@f5e63f1 // DIFFERENT

This happens because even though you’ve marked the @Component and the provider as @Singleton, it doesn’t mean that Dagger will create only one instance of the class.

Note: A Singleton in the Gang Of Four sense is a way to create a single instance of a class which is accessible from any point of an app. You can find more information here.

It’s your job to keep track of each component and its lifecycle. It’s time to learn how to do that.

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:

  1. Defines the appComponent, containing a reference to the instance which Dagger creates.
  2. Creates the instance of the AppComponent using create() on DaggerAppComponent. Remember that the DaggerAppComponent is an implementation generated by Dagger once you build your project.
  3. Defines appComp() to expose the appComponent.

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.

Note: You could put the same abstract method into the 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!

User selects a generic article title from list which then displays

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:

  1. The AppModule is now abstract.
  2. You need a companion object for the definitions of @Provides. It’s also important to annotate this object with @Module as well.
  3. Providers also annotated with @JvmStatic which is a Kotlin annotation useful if you want to make this function static, in the meaning of Java, in the companion object.
  4. Now you can use @Binds here.

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.

User selects a generic title from a list and the story displays

RwNews App

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:

  1. The AppModule now defines a constructor with a parameter of the type NewsRepository.
  2. The same is then used to create the instances that @Provides annotated 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:

  1. The DaggerAppComponent that Dagger provides now has a builder() which returns an implementation of a Component.Builder.
  2. You create an instance of the MemoryNewsRepository you need, to create the instance of the AppModule. You then pass to the AppComponent builder the created AppModule using appModule().
  3. The last method to invoke in the Builder implementation is called build>(). It returns the AppComponent implementation.

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.

User selects a generic title from a list and the story displays

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:

  1. You created an inner interface annotated with @Component.Builder. The name is usually Builder but it’s not important.
  2. 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.
  3. Finally, need to define a method whose name must be build which must have the component as the return type. In this case, the type is AppComponent.

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! :]

User selects a generic title from a list and the story displays

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.

User selects a generic title from a list and the story displays

RwNews App

Using the @Component.Factory Interface

Like the Builder, the Factory method is a creational GoF pattern. That means it defines some way of creating instances of a specific class. While the former provides you some methods in to pass what you need, the latter provides you a single method with multiple parameters.

The former defines a build() that the latter doesn’t need. The same difference happens when you use them with Dagger. To prove this, change the AppComponent.kt to the following:

@Component(modules = [AppModule::class])
interface AppComponent {

  fun inject(frag: NewsListFragment)

  fun inject(frag: NewsDetailFragment)

  // 1
  @Component.Factory
  interface Factory {
    // 2
    fun repository(@BindsInstance repo: NewsRepository): AppComponent
  }
}

Here you can see two main differences:

  1. The inner interface is now annotated with @Component.Factory.
  2. The interface defines a single method which has the AppCompomnent as return type. The parameter is of the type you need to pass and is annotated with @BindsInstance.

In this case, the factory method contains only one parameter but it could have many. You can see the different generated code in the InitApp class where the creation of the AppCompomnent has to be like this:

class InitApp : Application() {
  - - -
  override fun onCreate() {
    super.onCreate()
    appComponent = DaggerAppComponent
      .factory() // 1
      .repository(MemoryNewsRepository()) // 2
  }
  - - -
}

As you can see, Dagger creates a factory method for you which exposes the repository() you can invoke to pass the missing dependencies. Build and run the app.

User selects a generic title from a list and the story displays

RwNews App

You’ve now covered many ways you can build dependencies in Dagger! :]

Where to Go From Here

You can download the final result using the Download Materials button at the top or bottom of this tutorial.

In this tutorial, you configured Dagger to manage DI in several ways, depending on what information you provided and how they can be used to manage dependencies, change code generation performance and runtime dependency fetching.

You’re a Dagger master now, but you still need to learn one more step: How to manage dependencies between different @Component definitions with different @Scope. You’ll learn that in the second part of this tutorial! :]

I hope you found this helpful! If you have any comments or questions, feel free to join in the forum below.

Average Rating

4.8/5

Add a rating for this content

4 ratings

More like this

Contributors

Comments