Reputation: 1111
I am getting a dependency cycle whenever I try to use a subcomponent with binding objects. I have an app scope and an activity scope. At the app scope I create my web service then when the activity opens I want to create a storage object, controller, and navigator (all custom classes not androidx classes) and inject them into my androidx ViewModel class. But when I do so I get a dependency cycle.
My top level component looks like
@AppScope
@Component(modules = [AppModule::class])
interface AppComponent {
val activityComponentBuilder: ActivityComponent.Builder
}
@Module(subcomponents = [ActivityComponent::class])
interface AppModule {
@Binds
fun mockWebService(mockWebService: MockWebService): MockWebService
}
Next my subcomponent looks like
@ActivityComponent
@Subcomponent(modules = [ActivityModule::class])
interface ActivityComponent {
fun inject(sharedViewModel: SharedViewModel)
@Subcomponent.Builder
interface Builder {
@BindsInstance
fun storage(storage: Storage): Builder
fun build(): ActivityComponent
}
}
In my activity module I bind two objects
@Binds
abstract fun controller(controller: Controller): Controller
@Binds
abstract fun navigator(navigator: Navigator): Navigator
Each object has an @Inject constructor
class Navigator @Inject constructor(private val storage: Storage)
class Controller @Inject constructor(
private val webService: MockWebService,
private val navigator: Navigator,
private val storage: Storage
) {
Inside my shared view model I try to build my component and inject the fields
@Inject
lateinit var navigator: Navigator
@Inject
lateinit var controller: Controller
init {
MainApplication.component.activityComponentBuilder
.storage(InMemoryStorage.from(UUID.randomUUID().toString()))
.build()
.inject(this)
}
But dagger won't build. I get an error
[Dagger/DependencyCycle] Found a dependency cycle: public abstract interface AppComponent {
MockWebService is injected at di.AppModule.mockWebService(mockWebService)
MockWebService is injected at ActivityModule.Controller(webService, …)
Controller is injected at SharedViewModel.controller
SharedViewModel is injected at
But the error message cuts off there. Am I missing something in how to use a subcomponent to put objects on the graph and then inject them into an object? Is this not possible with Dagger?
Upvotes: 1
Views: 1724
Reputation: 3021
@Binds
is used to let dagger know the different implementations of an interface. You don't need @Binds
here since Navigator and Controller are simple classes that do not implement any interface. I'd assume that's the case with MockWebService too. Also, those classes have @Inject constructor
, which means dagger can instantiate them and we don't need to write extra @Provides
functions for those classes.
@Binds
isn't doing any scoping. Its only job is to tell dagger about different implementations. You can add @XScope
with @Binds
to make some object scoped. Or, you could just add the scope annotation to the class declaration. Here's an example of how you can add scope to class declaration.
As for the dependency cycle, I think it's because you're telling ActivityComponent
to use ActivityModule
and telling ActivityModule
to install ActivityComponent
. Doing just either one should be the case (I think).
Upvotes: 2