9

Ma question est similaire à this.Injection de dépendance de CustomView avec dagger 2 (dans la portée de l'activité)

Ainsi, par exemple, j'ai une LiveData mise en œuvre:

public class CustomLiveData extends LiveData<SomeEvent> { 

    @Inject 
    public CustomLiveData(@ActivityContext Context context) { 
     //.... 
    } 

} 

que je veux 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 de compilateur si CustomLiveData est injecté dans MainActivity à la place dans la vue. Merci!

+0

Avez-vous eu une erreur de compilation lors de l'injection de 'CustomLiveData' dans la vue' Custom'? Veuillez le poster avec votre question. –

+0

le message a été inclus dans la partie CustomView. Voir "// Obtention @ com.di.qualifiers.ActivityContext android.content.Context ne peut pas ..." – sinek

+0

Peut-on poster le '@ ActivityContext'? –

Répondre

13

tl; dr Ne pas injecter les dépendances de la couche modèle à l'intérieur des objets personnalisés View

de View sont sous-classes pas de bonnes cibles pour l'injection Dagger 2. View objets sont destinés à être dessinés et ne doit pas d'autre, d'où le nom "vue". Les constructeurs pour View devraient rendre ceci clair; Ils sont conçus pour gonfler les objets View à partir d'attributs spécifiés dans XML. En d'autres termes, un objet View devrait pouvoir être spécifié dans un fichier layout.xml, gonflé au point approprié du cycle de vie, puis obtenu en utilisant findViewById(int id), Butterknife ou la liaison de données. De cette façon, les meilleurs objets personnalisés View ne prennent aucune dépendance.

Si vous voulez lier un View et quelques données de la couche de modèle, le modèle standard est d'écrire un adaptateur comme ceux pour RecyclerView et ListView. Si cela n'est pas possible, l'utilisation d'un paramètre (par exemple, setData()) est préférable à la transmission de dépendances de la couche de modèle dans le constructeur ou à la demande d'injection à l'intérieur 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 Context correct sera fourni sans que vous ayez à faire quoi que ce soit. Cela explique votre commentaire "Je ne reçois aucune plainte de 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 accesseur) pour associer les données à votre objet personnalisé View.Voir l'exemple Google Android architecture here où les éléments de la couche de modèle sont injectés à l'aide Dagger 2, puis associé à un ListView en utilisant findViewById et setAdapter()

Lien vers le Dagger 2 problème où l'injection de View objets est discuté:

https://github.com/google/dagger/issues/720

1

Votre hiérarchie Dagger ressemble à ceci: appcomponent ->activitycomponent

Vous essayez d'injecter une activité context vue intérieure, qui dépend directement appcomponent.

Ce n'est pas possible car aucune méthode ne peut fournir le contexte d'activité dans appcomponent. Au lieu de cela, à l'intérieur de la vue, vous devez récupérer l'activité (par exemple en utilisant getContext), en extraire activitycomponent et ensuite seulement injecter CustomLiveData.