Reputation: 11
I have a BaseUiFragment in base module, need inject a UiComponent.
public abstract class BaseUiFragment extends Fragment {
@Inject
UiComponent mUiComponent;
@Override
public final void onAttach(Context context) {
AndroidSupportInjection.inject(this); //this is subclass
super.onAttach(context);
}
}
@Subcomponent
public interface BaseUiFragmentSubcomponent extends AndroidInjector<BaseUiFragment> {
@Subcomponent.Builder
abstract class Builder extends AndroidInjector.Builder<BaseUiFragment> {
}
}
@Module(subcomponents = BaseUiFragmentSubcomponent.class)
public abstract class BaseUiFragmentModule {
@Binds
@IntoMap
@FragmentKey(BaseUiFragment.class) // key in MapProviderFactory
abstract AndroidInjector.Factory<? extends Fragment>
bindBaseUiFragmentInjectorFactory(BaseUiFragmentSubcomponent.Builder builder);
private BaseUiFragmentModule() {}
}
In app module, UiComponentModule provide UiComponent, MainFragment extends BaseUiFragment.
@Module
public class UiComponentModule {
@Provides
static UiComponent provideUiComponent() {
return new UiComponent() {};
}
}
@Singleton
@Component(modules = {AndroidSupportInjectionModule.class, BaseUiFragmentModule.class, UiComponentModule.class})
public interface ApplicationComponent extends AndroidInjector<MainApplication> {
@Component.Builder
abstract class Builder extends AndroidInjector.Builder<MainApplication> {
}
}
public class MainFragment extends BaseUiFragment {
@Override
public View onCreateViewImpl(Bundle savedInstanceState) {
return new View(getContext());
}
}
when AndroidSupportInjection.inject(this); run, it does not work. Because DispatchingAndroidInjector's maybeInject() return false
injectorFactories has (BaseUiFragment.class, ...) not has (MainFragment.class, ...), but AndroidSupportInjection.inject(this); this is MainFragment.
public boolean maybeInject(T instance) {
Provider<AndroidInjector.Factory<? extends T>> factoryProvider =
injectorFactories.get(instance.getClass());
if (factoryProvider == null) { // factoryProvider is null
return false;
}
// ...
}
So, How to use AndroidInjection(AndroidSupportInjection) in base class?
After a few days of analysis:
Google's inject impl: it's only instance.getClass()
public boolean maybeInject(T instance) {
Provider<AndroidInjector.Factory<? extends T>> factoryProvider =
injectorFactories.get(instance.getClass());
if (factoryProvider == null) {
return false;
}
// ...
}
My impl: traversal it and its superclass,the problem is solved, but it use reflection that get the factoryProvider.
public boolean maybeInject(T instance) {
Provider<AndroidInjector.Factory<? extends Fragment>> factoryProvider
= injectorFactories.get(fragment.getClass());
Class fragmentSuperclass = fragment.getClass().getSuperclass();
while (factoryProvider == null && fragmentSuperclass != Fragment.class) {
factoryProvider = injectorFactories.get(fragmentSuperclass);
fragmentSuperclass = fragmentSuperclass.getSuperclass();
}
if (factoryProvider == null) {
return false;
}
// ...
}
So, is it only this way? And Google can Change the implementation?
Upvotes: 1
Views: 905
Reputation: 959
You have only created a subcomponent that knows how to inject BaseUiFragment
. Since that is all that dagger can see it will only know how to generate code to handle injecting the BaseUiFragment
.
You need to create a subcomponent for each leaf of your inheritance hierarchy.
Something like this is how i like to do my fragment components
@Subcomponent
public interface MainFragmentComponent extends AndroidInjector<MainFragment> {
@Subcomponent.Builder
abstract class Builder extends AndroidInjector.Builder<MainFragment> {}
@Module(subcomponents = MainFragmentComponent.class)
abstract class BindingModule {
@Binds
@IntoMap
@FragmentKey(MainFragment.class)
abstract Factory<? extends Fragment> mainFragmentComponentBuilder(Builder impl);
}
}
Upvotes: 2