François Legrand
François Legrand

Reputation: 1223

How to avoid circular dependency with Dagger 2?

I have the following Module :

@Module
class HomeModule(private val context: Context) {

    @Provides
    fun provideContext() = context

    @Provides
    fun provideHomeUi(): HomeUi {
        return HomeUi()
    }

    @Provides
    @Singleton
    fun provideHomePresenter(homeUi: HomeUi): HomePresenter {
        return HomePresenter(homeUi)
    }
}

Those injected fields in HomeUi.kt

@Inject lateinit var context: Context
@Inject lateinit var presenter: HomePresenter

And this one in HomePresenter.kt

@Inject lateinit var context: Context

Here my Deps Component

@Singleton
@Component(modules = arrayOf(
        NetworkModule::class,
        HomeModule::class
))
interface Deps {
    fun inject(homePresenter: HomePresenter)
    fun inject(homeActivity: HomeActivity)
    fun inject(homeUi: HomeUi)
} 

I am using Dagger 2.10 but a StackOverflowError is thrown. I am looking for a way to avoid my circular dependency.

Note : This is my HomeUi which is infinitely instantiate.

Upvotes: 0

Views: 3338

Answers (1)

David Medenjak
David Medenjak

Reputation: 34532

It seems like you'd be calling field injection on HomeUi from within your presenters constructor, thus triggering an infinite loop since neither object can finish being constructed without the other (?). This looks like a really bad approach and you should try to move your dependencies into the objects constructors instead of creating half-finished objects.

Use field injection primarily for objects that you can't create yourself, e.g. with Android framework types. IMHO inject(homeActivity: HomeActivity) should be the only method of your component.

Cyclic dependencies are hard to manage and there is no perfect solution, but you can try things like switching to Provider<HomePresenter> to delay the dependency and be able to resolve it this way.

The following should do what you intended, and please note how I'm using constructor injection instead of having 2 additional methods in the module.

@Singleton
@Component(modules = arrayOf(
    NetworkModule::class,
    HomeModule::class
))
interface Deps {
  fun inject(homeActivity: HomeActivity)
}

@Module
class HomeModule(private val context: Context) {

  @Provides
  fun provideContext() = context

}

@Singleton
class HomeUi @Inject constructor(presenter : Provider<HomePresenter>, context : Context)
{
  // use with presenter.get()
}

@Singleton
class HomePresenter @Inject constructor(homeUi : HomeUi)

Please note that using a Provider<T> is the cheapest way to resolve a cyclic dependency that I know of, but it might not be suited for every situation.

Upvotes: 5

Related Questions