We are attempting to develop a new Activity for an existing Fragment, the compiler is throwing errors for an existing FragmentComponentBuilder class that other Dagger modules have had no issues with utilizing in the past, but because of the code we introduced, is now failing to build. The error raised for the FragmentComponentBuilder details not being able to be provided because of a missing @Provides-annotated, also binding with matching key existing in components related to the new Activity.
Here is the error:
An important part about the new DevicesActivity we're developing, is that the associated DevicesFragment logic needs to remain relatively unchanged, because the core logic that handles the presentation of this screen is a Fragment -> Fragment interaction. The DevicesActivity handles presentation from another Activity, and contains restricted view properties as opposed to the Fragment -> Fragment presentation.
The newly created DevicesActivity class:
DevicesActivityComponent for new Activity:
@scOpe
The DevicesActivityModule for new Activity:
The associated DevicesFragment class:
The CaComponent class that the error output references as a class that contains a binding with a matching key to other components:
@scOpe
FragmentComponentBuilder that has remained unchanged:
ActivityComponentBuilder that has remained unchanged:
AppComponent that contains a newly created inject method that takes the DevicesActivity as a parameter:
@Singleton
Please let me know if you need to see any additional files not included here that would assist in troubleshooting. Thank you for whatever help you're willing to give!
Here is the error:
Code:
/app/application/AppComponent.java:8: error: [Dagger/MissingBinding] java.util.Map<java.lang.Class<? extends androidx.fragment.app.Fragment>,javax.inject.Provider<app.dagger.fragment.FragmentComponentBuilder<?,?>>> cannot be provided without an @Provides-annotated method.
public abstract interface AppComponent {
^
A binding with matching key exists in component: app.CaComponent
java.util.Map<java.lang.Class<? extends androidx.fragment.app.Fragment>,javax.inject.Provider<app.dagger.fragment.FragmentComponentBuilder<?,?>>> is injected at
app.DevicesActivity.fragmentComponentBuilders
app.DevicesActivity is injected at
app.application.AppComponent.inject(app.DevicesActivity)
It is also requested at:
app.DevicesActivity.fragmentComponentBuilders
The following other entry points also depend on it:
dagger.MembersInjector.injectMembers(T) [app.application.AppComponent → app.DevicesActivityComponent]
An important part about the new DevicesActivity we're developing, is that the associated DevicesFragment logic needs to remain relatively unchanged, because the core logic that handles the presentation of this screen is a Fragment -> Fragment interaction. The DevicesActivity handles presentation from another Activity, and contains restricted view properties as opposed to the Fragment -> Fragment presentation.
The newly created DevicesActivity class:
Code:
class DevicesActivity: InjectableActivity(), CaView, HasFragmentSubComponentBuilders{
[USER=264617]@INJECT[/USER]
lateinit var fragmentComponentBuilders: Map<Class<out Fragment>, @JvmSuppressWildcards Provider<FragmentComponentBuilder<*, *>>>
var caModel: CaModel? = null
private val devicesFragment = DevicesFragment.newInstance()
companion object {
val kCaModel = "CA_MODEL_KEY"
fun create(context: Context, caModel: CaModel) : Intent {
val intent = Intent(context, DevicesActivity::class.java)
intent.putExtra(kCaModel, caModel)
return intent
}
}
override fun injectActivity(hasActivitySubComponentBuilders: HasActivitySubcomponentBuilders): ActivityComponent<DevicesActivity> {
val builder = hasActivitySubComponentBuilders.getBuilder(DevicesActivity::class.java)
val componentBuilder = builder as DevicesActivityComponent.Builder
val component = componentBuilder.activityModule(DevicesActivityModule())?.build()
component!!.injectMembers(this)
return component
}
override fun onStart(){
super.onStart()
supportFragmentManager.beginTransaction().replace(android.R.id.content, devicesFragment).commit()
}
override fun getBuilder(fragmentClass: Class<out Fragment>): FragmentComponentBuilder<*, *> =
fragmentComponentBuilders.getValue(fragmentClass).get()
override fun onLoggedOut(wasLoggedOutDueToInactivity: Boolean, wasLoggedOutDueToBadCreds: Boolean) {
}
}
DevicesActivityComponent for new Activity:
@scOpe
Code:
@Retention(AnnotationRetention.RUNTIME)
annotation class DevicesScope
@DevicesScope
@Subcomponent(modules = [DevicesActivityModule::class])
interface DevicesActivityComponent : ActivityComponent<DevicesActivity> {
@Subcomponent.Builder
interface Builder : ActivityComponentBuilder<DevicesActivityModule, DevicesActivityComponent>
}
The DevicesActivityModule for new Activity:
Code:
@Module
class DevicesActivityModule : ActivityModule<DevicesActivity>() {
@Provides
fun presenter(caSubject: CaSubject, interactor: ST)
: DevicesPresenter = DevicesPresenter(caSubject, interactor)
@Provides
fun interactor(schedulerProvider: SchedulerProvider) : ST =
ST(schedulerProvider)
}
The associated DevicesFragment class:
Code:
class DevicesFragment : MvpScopedFragment<DevicesView, DevicesPresenter>(), DevicesView {
lateinit var binding : DevicesBinding
private lateinit var adapter : DevicesAdapter
lateinit var devicesDialog: AlertDialog
companion object {
fun newInstance() : DevicesFragment = DevicesFragment()
}
override fun inject(hasFragmentSubComponentBuilders: HasFragmentSubComponentBuilders): DevicesPresenter {
val builder = hasFragmentSubComponentBuilders.getBuilder(DevicesFragment::class.java)
val componentBuilder = builder as DevicesFragmentComponent.Builder
val component = componentBuilder.module(DevicesFragmentModule(this)).build()
return component.presenter()
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
binding = DataBindingUtil.inflate(inflater, R.layout.respec_devices, container, false)
return binding.root
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
presenter.caModel.screen = CaScreen.DEVICES
presenter.save()
binding.header.caModel = presenter.caModel
binding.caModel = presenter.caModel
presenter.setDelegate()
if(presenter.pm.hasProfile()) {
presenter.profile = presenter.pm.getProfile()!!
}
}
The CaComponent class that the error output references as a class that contains a binding with a matching key to other components:
@scOpe
Code:
@Retention(AnnotationRetention.RUNTIME)
annotation class CaScope
@CaScope
@Subcomponent(modules = [CaModule::class, FragmentBindingModule::class])
interface CaComponent : ActivityComponent<CaActivity> {
fun presenter() : CaPresenter
@Subcomponent.Builder
interface Builder : ActivityComponentBuilder<CaModule, CaComponent>
}
FragmentComponentBuilder that has remained unchanged:
Code:
interface FragmentComponentBuilder<M : FragmentModule<*>, C : FragmentComponent<*>> {
fun module(activiyModule: M) : FragmentComponentBuilder<M, C>
fun build() : C
}
ActivityComponentBuilder that has remained unchanged:
Code:
interface ActivityComponentBuilder<M : ActivityModule<*>, C : ActivityComponent<*>> {
fun activityModule(activityModule: M): ActivityComponentBuilder<M, C>?
fun build(): C
}
AppComponent that contains a newly created inject method that takes the DevicesActivity as a parameter:
@Singleton
Code:
@Component(modules = [AppModule::class, ActivityBindingModule::class])
interface AppComponent {
fun inject(app: App)
fun inject(printingService: PrintingService)
fun context() : Context
fun preferenceFactory() : PreferenceFactory
fun inject(devicesActivity: DevicesActivity)
}
Please let me know if you need to see any additional files not included here that would assist in troubleshooting. Thank you for whatever help you're willing to give!
Last edited: