Dagger 2:如何从FragmentFactory的子组件绑定父组件中的Fragment Map

Mat*_*Pag 6 android android-fragments dagger dagger-2

我有这个Dagger 2配置:

AppComponent.kt

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

ActivityBindingModule.kt

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

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
}
Run Code Online (Sandbox Code Playgroud)

片段模块

@Module
abstract class FragmentModule {
    @Binds
    internal abstract fun bindFragmentFactory(factory: DefaultFragmentFactory): FragmentFactory
}
Run Code Online (Sandbox Code Playgroud)

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()
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

我在这里尝试做的是使用FragmentFactory来创建Fragments。如代码中所述,我可以取消注释以下行:

@Binds
@IntoMap
@FragmentKey(LoginFragment::class)
abstract fun bindLoginFragment(loginFragment: LoginFragment): Fragment
Run Code Online (Sandbox Code Playgroud)

并删除所有与之相连的部件,LoginFragmentSubcomponent注射工作正常。

这种方法的问题在于,无法正确定义Fragment的范围,如果Fragment被销毁,则下面的所有子Fragment都将被销毁,这将迫使Factory在下一次调用时创建新实例。

因此,我决定添加一个Subcomponent名为LoginFragmentSubcomponent的对象,以便能够在其@FragmentScoped上进行定义。现在的问题是,在模块中,LoginFragmentModule我无法为Map我需要DefaultFragmentFactory的类型提供帮助的父项:

Map<Class<out Fragment>, @JvmSuppressWildcards Provider<Fragment>>
Run Code Online (Sandbox Code Playgroud)

实际上,我在编译时收到的错误是:

[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.
Run Code Online (Sandbox Code Playgroud)

这里有一个带有可重现问题的示例仓库https : //github.com/matpag/dagger-test-fragmentfactory

我该如何解决? 我确定我做错了什么,因为我仍在学习Dagger 2和/或我不了解问题的某些部分以及它应该如何工作。

使用匕首2.21

提前致谢

PS:此次FragmentFactory注入背后的想法来自于这篇很棒的帖子:https : //www.captechconsulting.com/blogs/using-androidxs-fragmentfactory-with-dagger-for-fragment-dependency-injection我只是对其进行了一些调整。