Dagger 2 : How to bind Fragment Map in parent component from subcomponent for FragmentFactory

可紊 提交于 2019-12-21 12:26:15

问题


I have this Dagger 2 configuration:

AppComponent.kt

@Singleton
@Component(
    modules = [
        AndroidSupportInjectionModule::class,
        AppModule::class,
        ActivityBindingModule::class,
    ]
)
interface AppComponent : AndroidInjector<AppApplication> {
    @Component.Builder
    abstract class Builder : AndroidInjector.Builder<AppApplication>()
}

ActivityBindingModule.kt

@Module
abstract class ActivityBindingModule {
    @ActivityScoped
    @ContributesAndroidInjector(
        modules = [
            MainActivityModule::class,
            FragmentModule::class //a fragment factory for each activity
        ]
    )
    internal abstract fun mainActivity(): MainActivity
}

MainActivityModule.kt

@Module(subcomponents = [LoginFragmentSubcomponent::class])
abstract class MainActivityModule {
    //uncommenting this works fine but Fragment can't be scoped
    //@Binds
    //@IntoMap
    //@FragmentKey(LoginFragment::class)
    //abstract fun bindLoginFragment(loginFragment: LoginFragment): Fragment

    //...other things which work fine
}

@Subcomponent(modules = [LoginFragmentModule::class])
@FragmentScoped
interface LoginFragmentSubcomponent : AndroidInjector<LoginFragment> {
    @Subcomponent.Builder
    abstract class Builder : AndroidInjector.Builder<LoginFragment>()
}

@Module
abstract class LoginFragmentModule {
    //this can't be seen from Map used in DefaultFragmentFactory
    @Binds
    @IntoMap
    @FragmentKey(LoginFragment::class)
    abstract fun bindLoginFragment(loginFragment: LoginFragment): Fragment
}

FragmentModule.kt

@Module
abstract class FragmentModule {
    @Binds
    internal abstract fun bindFragmentFactory(factory: DefaultFragmentFactory): FragmentFactory
}

DefaultFragmentFactory.kt

@ActivityScoped
class DefaultFragmentFactory @Inject constructor(
    private val creators: Map<Class<out Fragment>, @JvmSuppressWildcards Provider<Fragment>>
) : FragmentFactory() {
    //this is deprecated in fragment-ktx 1.1.0-alpha06 
    //but you need to override this if you were using the bundle
    override fun instantiate(classLoader: ClassLoader, className: String, args: Bundle?): Fragment {
        return instantiate(classLoader, className).apply { arguments = args }
    }
    override fun instantiate(classLoader: ClassLoader, className: String): Fragment {
        val fragmentClass = loadFragmentClass(classLoader, className)
        val found = creators.entries.find { fragmentClass.isAssignableFrom(it.key) }
        val provider = found?.value
        //if we don't find a match in the map, proceed with the default empty constructor
        return if (provider != null) {
            provider.get()
        } else {
            fragmentClass.newInstance()
        }
    }
}

What I'm trying to do here is to use the FragmentFactory to create the Fragments. As described in the code, I can uncomment this lines:

@Binds
@IntoMap
@FragmentKey(LoginFragment::class)
abstract fun bindLoginFragment(loginFragment: LoginFragment): Fragment

and removing everything connected to LoginFragmentSubcomponent and the injection is working fine.

The problem with this approach is that the Fragment can't be scoped properly, in a way that if the Fragment is destroyed all the child Fragments below are destroyed which will force the Factory to create a new instance at the next call.

So I've decided to add a Subcomponent called LoginFragmentSubcomponent to be able to define the @FragmentScoped on it. Now the problem is that from the module LoginFragmentModule I can't contribute to the parent Map I need in DefaultFragmentFactory which is of type:

Map<Class<out Fragment>, @JvmSuppressWildcards Provider<Fragment>>

In fact the error I'm receiving at compile time is:

[Dagger/MissingBinding] 
java.util.Map<java.lang.Class<? extends androidx.fragment.app.Fragment>,
javax.inject.Provider<androidx.fragment.app.Fragment>> cannot be provided 
without an @Provides-annotated method.

Here there is a sample repo with the reproducible problem: https://github.com/matpag/dagger-test-fragmentfactory

How can I solve this? I'm sure I'm doing something wrong because I'm still learning Dagger 2 and/or I didn't understand some part of the problem and how this should work.

Using Dagger 2.21

Thanks in advance

PS: The idea behind this FragmentFactory injection came from this awesome post: https://www.captechconsulting.com/blogs/using-androidxs-fragmentfactory-with-dagger-for-fragment-dependency-injection I just tweaked it a bit.

来源:https://stackoverflow.com/questions/55043890/dagger-2-how-to-bind-fragment-map-in-parent-component-from-subcomponent-for-fr

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!