Alexandr
Alexandr

Reputation: 3969

Dagger 2: Multiple entries with same key

Short explanation

Simplified UML diagram that describes by Components architecture.

enter image description here

And there is an exception:

java.lang.IllegalArgumentException: 
Multiple entries with same key: 
gson=com.example.di.AppPresentationComponent and
gson=com.example.di.gamesession.GameSessionComponent

Dagger 2 cannot decide from which Component provide an Gson instance.

I never ever work with multiple inheritance. What can you recommend to solve my situation?

I can dispose of GameSessionComponent and move gameSessionManager() to GameSessionPresentationComponent. It will work in my case, but it's sounds like a dirty solution.

There is some code.

AppComponent:

@PerApplication
@Component(modules = arrayOf(
        AppModule::class,
        TextsModule::class,
        AudioModule::class,
        FontsModule::class,
        TexturesModule::class,
        QuestModule::class,
        GameSaveModule::class
))
interface AppComponent {
    fun componentsHolder(): ComponentsHolder
    fun gdxContext(): GdxContext
    fun rxHelper(): RxHelper
    fun textsManager(): TextsManager
    fun soundsManager(): AudioManager
    fun fontsManager(): FontsManager
    fun texturesManager(): ThemesManager
    fun questManager(): QuestManager
    fun gameSaveManager(): GameSaveManager
}

AppPresentationComponent

@PerApplicationPresentation
@Component(
        dependencies = arrayOf(AppComponent::class),
        modules = arrayOf(AppPresentationModule::class)
)
interface AppPresentationComponent : AppComponent {
    fun fonts(): Fonts
    fun audio(): Audio
    fun texts(): Texts
    fun textures(): Textures
    fun router(): KRQRouter
    fun launch(): LaunchScreen
    fun mainMenu(): MenuScreen
    fun settingsDialog(): SettingsDialog
    fun questInfoDialog(): InfoDialog
}

GameSessionComponent

@PerGameSession()
@Component(
        dependencies = arrayOf(AppComponent::class),
        modules = arrayOf(GameSessionModule::class)
)
interface GameSessionComponent : AppComponent {
    fun storyTeller(): StoryTeller
}

GameSessionPresentationComponent

@PerGameSessionPresentation
@Component(
        dependencies = arrayOf(AppPresentationComponent::class),
        modules = arrayOf(GameSessionPresentationModule::class, GameSessionModule::class)
)
interface GameSessionPresentationComponent : AppPresentationComponent {
    fun storyTeller(): StoryTeller

    fun story(): StoryScreen
    fun gameoverDialog(): GameoverDialog
    fun inGameMenu(): InGameMenu
    fun donateDialog(): DonateDialog
}

Upvotes: 1

Views: 2437

Answers (1)

David Rawson
David Rawson

Reputation: 21427

The Dagger user guide talks about two types of bindings:

Published bindings are those that provide functionality that is used by other parts of the application.

Internal bindings are the rest: bindings that are used in the implementation of some published type and that are not meant to be used except as part of it. These bindings are usually for package-private types or are qualified with package- private qualifiers.

You probably need to be thinking of this when you design your modules and components. What you currently have:

interface AppPresentationComponent : AppComponent
interface GameSessionComponent : AppComponent
interface GameSessionPresentationComponent : AppPresentationComponent

is a hierarchy where every component publishes all their bindings to the dependent components. This is quite confusing especially since your hierarchy of interfaces does not follow the hierarchy of components to dependent components. Set up like this, it is easy to get a duplicate binding. Ideally, you should not reach a situation where you have to reason about multiple inheritance for components.

At this stage, I would suggest you refactor your component interfaces so that they don't inherit from each other and you will be able to discover which bindings need to be published to dependent components and which can (and should) be internalized.

The same applies for your modules. Like a component, a module can both publish and internalize a binding. Why not bind GSON in another module at the ApplicationComponent level and publish it to dependent components? Then you can get rid of the binding for GSON inside GameSessionModule which seems to be causing the problem.

Upvotes: 2

Related Questions