Reputation: 44118
I'm trying to use android-dagger to inject a fragment from a manually defined subcomponent:
@Component(modules = [
AndroidSupportInjectionModule::class,
AppModule::class,
BuilderModule::class
])
interface AppComponent : AndroidInjector<App> {
@Component.Builder
abstract class Builder : AndroidInjector.Builder<App>()
fun someComponent(): SomeComponent
}
@Subcomponent
interface SomeComponent {
fun inject(fragment: SomeFragment)
}
Execution fails with:
IllegalArgumentException: No injector factory bound for Class "SomeFragment"
However, if I create a fragment bind annotated with @ContributesAndroidInjector
it executes fine. The doc states that all this does is create a subcomponent. Why can't I do that manually?
Minimal working project can be found on github: https://github.com/absimas/fragment-injection
Upvotes: 2
Views: 344
Reputation: 95634
@ContributesAndroidInjector
creates a subcomponent, yes. The docs don't say anything more, but they don't assert that this only creates a subcomponent; it also installs it into the Map<Class, AndroidInjector.Factory>
multibinding that powers each of dagger.android's injection types.
You can see an example of this map binding on the Android page of the Dagger User's Guide:
@Binds
@IntoMap
@FragmentKey(YourFragment.class)
abstract AndroidInjector.Factory<? extends Fragment>
bindYourFragmentInjectorFactory(YourFragmentSubcomponent.Builder builder);
Note that this expects you to bind a Builder, not a Component: dagger.android expects that you'll want access to your Fragment instance from within your subcomponent, so the binding is for AndroidInjector.Factory<? extends Fragment>
such that DispatchingAndroidInjector can call create(Fragment)
on it. This is designed to be compatible with Subcomponent.Builder
, but it does require that you define your @Subcomponent.Builder
nested interface that extends the adapter AndroidInjector.Builder
. Pardon my Java on a Kotlin question:
/** No need for an explicit inject, because you must extend AndroidInjector. */
@Subcomponent
interface SomeComponent extends AndroidInjector<SomeFragment> {
/**
* This abstract class handles the final create(Fragment) method,
* plus @BindsInstance.
*/
@Subcomponent.Builder
abstract class Builder extends AndroidInjector.Builder<SomeFragment> {}
}
EDIT: It occurs to me now that your title states you don't want a dedicated subcomponent; beyond using a manual definition instead of @ContributesAndroidInjector
, if you want a multi-Fragment subcomponent, then you might run into some trouble with this advice: dagger.android requires implementations of AndroidInjector<YourFragment>
, and because of erasure, you own't be able to have a single class implement multiple AndroidInjector<T>
or AndroidInjector.Builder<T>
interfaces or abstract classes. In those cases you might need to write a manual AndroidInjector.Factory implementation which calls the correct concrete inject
method. However, this seems like a lot of work for the sake of creating a monolithic Fragment component, when best-practices dictate dagger.android's default: a small and specific component for each Fragment.
Upvotes: 1