2017-05-21 1 views
0

J'ai un BaseUiFragment dans le module de base, j'ai besoin d'injecter un UiComponent.Comment utiliser AndroidInjection dans la classe de base?

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() {} 
} 

Dans le module d'application, UiComponentModule fournir UIComponent, MainFragment étend 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()); 
    } 
} 

lorsque AndroidSupportInjection.inject (this); courir, ça ne marche pas. Parce que maybeInject() return false

injectorFactories de DispatchingAndroidInjector a (BaseUiFragment.class, ...) pas a (MainFragment.class, ...), mais AndroidSupportInjection.inject (this); c'est MainFragment.

public boolean maybeInject(T instance) { 
    Provider<AndroidInjector.Factory<? extends T>> factoryProvider = 
     injectorFactories.get(instance.getClass()); 
    if (factoryProvider == null) { // factoryProvider is null 
     return false; 
    } 
    // ... 
} 

Alors, Comment utiliser AndroidInjection (AndroidSupportInjection) dans la classe de base?

Après quelques jours d'analyse:

injectent de Google impl: il est seulement instance.getClass()

public boolean maybeInject(T instance) { 
    Provider<AndroidInjector.Factory<? extends T>> factoryProvider = 
      injectorFactories.get(instance.getClass()); 
    if (factoryProvider == null) { 
     return false; 
    } 
    // ... 
} 

Mon impl: traversal et sa superclasse, le problème est résolu , mais il utilise la réflexion qui obtient le 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; 
    } 
    // ... 
} 

Alors, est-ce que cette façon? Et Google peut changer la mise en œuvre?

Répondre

2

Vous avez seulement créé un sous-composant qui sait comment injecter BaseUiFragment. Puisque c'est tout ce que le poignard peut voir, il ne saura comment générer du code pour gérer l'injection du BaseUiFragment.

Vous devez créer un sous-composant pour chaque feuille de votre hiérarchie d'héritage.

Quelque chose comme ceci est comment j'aime faire mes composants fragment

@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); 
    } 
} 
+0

« créer un sous-composant pour chaque feuille de votre hiérarchie d'héritage » est pas une bonne façon, je pense. ce n'est pas sympathique pour l'appelant qui utilise ma bibliothèque de base. –

+0

@ApolunorJing Dagger est une solution de génération de code _compile-time. Si 'FooFragment' a un champ' @Inject Bar bar', Dagger ne peut pas le peupler sans l'inspecter au moment de la compilation (donc il peut écrire un setter pour cela), et la façon de le dire est exactement comme Trev l'a ici. +1 à la réponse –