Ma question est similaire à this .
Ainsi, par exemple, j'ai une implémentation LiveData
:
public class CustomLiveData extends LiveData<SomeEvent> {
@Inject
public CustomLiveData(@ActivityContext Context context) {
//....
}
}
que je souhaite injecter dans une vue personnalisée:
public class CustomView extends View {
@Inject
SomeApplicationProvider anyProvider;
@Inject
CustomLiveData dataProvider;
// Getting @com.di.qualifiers.ActivityContext Android.content.Context cannot be provided without an @Provides-annotated method.
// @com.di.qualifiers.ActivityContext Android.content.Context is injected at com.repositories.CustomLiveData.<init>(context)
// com.repositories.CustomLiveData is injected at com.ui.CustomView.dataProvider com.ui.CustomView is injected at
// com.di.ApplicationComponent.inject(view)
public CustomView(Context context) { this(context, null); }
public CustomView(Context AttributeSet attrs) {
super(context, attrs);
// Works ok for application provider
Application.getComponent(context).inject(this);
}
}
Et voici le reste des classes DI:
@ApplicationScope
@Component(
modules = {AndroidInjectionModule.class,
ActivityBuilder.class
})
public interface ApplicationComponent extends AndroidInjector<MyApp> {
void inject(MyApp application);
void inject(CustomView view);
@Component.Builder
abstract class Builder extends AndroidInjector.Builder<MyApp> {
public abstract ApplicationComponent build();
}
}
@ActivityScope
@Module (subcomponents = MainActivitySubcomponent.class)
public abstract class ActivityBuilder {
@Binds
@IntoMap
@ActivityKey(MainActivity.class)
abstract AndroidInjector.Factory<? extends Activity>
bindActivityInjectorFactory(MainActivitySubcomponent.Builder builder);
//...
}
@Subcomponent(modules = {MainActivityModule.class})
public interface MainActivitySubcomponent extends AndroidInjector<MainActivity> {
@Subcomponent.Builder
abstract class Builder extends AndroidInjector.Builder<MainActivity> {
}
}
@ActivityScope
@Module
public class MainActivityModule {
@Provides
@ActivityContext
public Context provideActivityContext(MainActivity activity) {
return activity;
}
// Seems to be wrong or not enough!?
@Provides
public CustomLiveData provideCustomLiveData(@ActivityContext Context context) {
return new CustomLiveData(context);
}
}
@Qualifier
public @interface ActivityContext{
}
Notez que je ne reçois aucune plainte du compilateur si CustomLiveData
est injecté dans MainActivity
à la place dans la vue. Merci!
View
personnalisésLes sous-classes de View
ne sont pas de bonnes cibles pour l'injection de Dagger 2. Les objets View
sont destinés à être dessinés et ne doivent pas l'être autrement, d'où le nom "view". Les constructeurs de View
devraient clarifier cela; ils sont conçus pour gonfler les objets View
à partir des attributs spécifiés en XML. En d'autres termes, un objet View
doit pouvoir être spécifié dans un fichier layout.xml
, Gonflé au point approprié du cycle de vie, puis obtenu à l'aide de findViewById(int id)
, - Butterknife ou liaison de données. De cette façon, les meilleurs objets View
personnalisés ne prennent aucune dépendance.
Si vous souhaitez lier un View
et certaines données de la couche modèle, le modèle standard consiste à écrire un adaptateur comme ceux pour RecyclerView
et ListView
. Si cela n'est pas possible, l'utilisation d'un setter (par exemple, setData()
) est préférable à la transmission des dépendances de la couche modèle dans le constructeur ou à la demande d'injection à partir de l'une des méthodes de cycle de vie du View
.
Si, à la place, vous injectez votre objet LiveData
dans une activité ou un fragment en utilisant la classe AndroidInjector
, le bon Context
sera fourni sans que vous ayez à faire quoi que ce soit. Cela explique votre commentaire "Je ne reçois aucune plainte du compilateur si CustomLiveData est injecté dans MainActivity à la place dans la vue."
Une fois que vous avez injecté l'objet LiveData
dans l'activité, utilisez l'une des méthodes ci-dessus (un adaptateur ou un setter) pour associer les données à votre View
personnalisé. Voir l'exemple Google Android Architecture ici où les éléments de la couche modèle sont injectés à l'aide de Dagger 2 puis associés à un ListView
à l'aide de findViewById
et setAdapter()
Lien vers le problème de Dagger 2 où l'injection d'objets View
est discutée:
Votre hiérarchie Dagger ressemble à ceci: appcomponent
-> activitycomponent
Vous essayez d'injecter l'activité context
dans la vue intérieure, qui dépend directement de appcomponent
.
Ce n'est pas possible car il n'y a aucune méthode qui pourrait fournir un contexte d'activité dans appcomponent
. Au lieu de cela, dans la vue intérieure, vous devez récupérer l'activité (par exemple en utilisant getContext
), en extraire activitycomponent
et ensuite seulement injecter CustomLiveData
.