https://kotlinlang.org logo
#android
Title
# android
t

Tim Malseed

02/11/2020, 11:51 PM
I’m having some trouble refactoring an Android codebase to make use of Square’s
@AssistedInject
with Dagger 2, though I’ve used it with success in other projects.. It seems as though the `@AssistedInject.Factory Factory`is not being provided its non-assisted properties. I don’t quite have the terminology to describe this problem, so I’ll show rather than tell:
The error I’m getting (abridged):
Copy code
MissingBinding (BridgeSelectionController), FragmentManager cannot be provided without @Provides method. A binding with matching key exists in component FragmentComponent

javax.inject.Provider<androidx.fragment.app.FragmentManager> is injected at
          <http://au.com|au.com>.smartsnugg.ui.screens.bridge_onboarding.bridgeselection.BridgeSelectionNavigator_AssistedFactory.<init>(fragmentManager)
The thing is,
fragmentManager
is provided via `NavigatorModule`:
Copy code
class BridgeSelectionController { ...

    @Inject
    lateinit var navigatorFactory: BridgeSelectionNavigator.Factory

    ...

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        (context!!.applicationContext as SmartSnuggApplication).appComponent
            .plus(ActivityModule(activity!!))
            .plus(NavigatorModule(fragmentManager!!))
            .inject(this)
BridgeSelectionNavigator
looks like so:
Copy code
class BridgeSelectionNavigator @AssistedInject constructor(
    fragmentManager: FragmentManager,
    @Assisted private val subject: Subject
) : BaseNavigator(fragmentManager), BridgeSelectionContract.Navigator {

    @AssistedInject.Factory
    interface Factory {
        fun create(subject: Subject): BridgeSelectionNavigator
    }
So, my thinking is that
BridgeSelectionController
uses
plus NavigatorModule
to construct a component that can provide an instance of FragmentManager. Then,
BridgeSelectionNavigator.Factory
is injected, and the required property
FragmentManager
should be supplied accordingly.
What gives? If I made
fragmentManager
an
@Assisted
property in the
BridgeSelectionNavigator
Factory, and then used
@Inject fragmentManager
in
BridgeSelectionController
, I’d be able to pass the
fragmentManager
to the
Factory.create()
method. But Dagger should do this for me - I should only have to provide
@Assisted
properties myself, Dagger should be able to provide the rest.
b

Barco

02/11/2020, 11:59 PM
What do your Component and Module look like?
t

Tim Malseed

02/12/2020, 12:00 AM
`FragmentComponent`:
Copy code
@FragmentScope
@Subcomponent(modules = [NavigatorModule::class, PresenterModule::class])
interface FragmentComponent {

...

    fun inject(target: BridgeSelectionController)

...
}
NavigatorModule
(I apologise for this mess):
Copy code
@Module(includes = [NavigatorModule.AbsNavigatorModule::class])
class NavigatorModule(
    private val fragmentManager: FragmentManager,
    @Named(CHILD_FRAGMENT_MANAGER) private val childFragmentManager: FragmentManager? = null,
    @Named(PARENT_FRAGMENT_MANAGER) private val parentFragmentManager: FragmentManager? = null
) {

    @Provides
    fun provideFragmentManager(): FragmentManager {
        return fragmentManager
    }

    @Named(CHILD_FRAGMENT_MANAGER)
    @Provides
    fun provideChildFragmentManager(): FragmentManager? {
        return childFragmentManager
    }

    @Named(PARENT_FRAGMENT_MANAGER)
    @Provides
    fun provideParentFragmentManager(): FragmentManager? {
        return parentFragmentManager
    }

    @Provides
    fun provideNavigator(): Navigator {
        return BaseNavigator(fragmentManager)
    }

    @Provides
    @Named(PARENT_NAVIGATOR)
    fun provideParentNavigator(): Navigator? {
        return parentFragmentManager?.let { BaseNavigator(parentFragmentManager) }
    }

...
(The interesting part of
NavigatorModule
, IMO is just:
Copy code
@Provides
    fun provideFragmentManager(): FragmentManager {
        return fragmentManager
    }
(It does indeed provide a FragmentManager)
b

Barco

02/12/2020, 1:25 AM
I don’t see where you are referencing the generated module
t

Tim Malseed

02/12/2020, 2:11 AM
Where I’m referencing the
AppAssistedModule
?
In
AppComponent:
Copy code
@Singleton
@Component(modules = [AppModule::class, AppAssistedModule::class, NetworkingModule::class, EventModule::class])
interface AppComponent {
...
b

Barco

02/12/2020, 3:12 AM
The generated module should have the format
AssistedInject_YourModule
@You should have at least one module annotated with
@AssistedModule
, which will indicate to Dagger to generate a module called
AssistedInject_YourModule
, which in turn will satisfy the dependency on your Factory
Then you just include this generated module in your normal module using the
includes
parameter in the
@Module
annotation.
t

Tim Malseed

02/12/2020, 3:47 AM
Yes, I am doing this
Copy code
@AssistedModule
@Module(includes = [AssistedInject_AppAssistedModule::class])
abstract class AppAssistedModule
I don’t believe the problem I’m having is due to a misconfiguration of AssistedInject
a

Ahmed Ibrahim

02/12/2020, 9:50 AM
From the error that you're getting, I think you already have another`@Provides` method that provides
FragmentManager
somewhere in your graph. If I were you I would check `ActivityModule`otherwise I would check the parent component of the
FragmentComponent
since it is a
@Subcomponent
t

Tim Malseed

02/12/2020, 10:05 AM
OK, thanks for the tip. It’s late here, but I’ll have a look tomorrow