9

Moje pytanie jest podobne do this.Wstrzyknięcie zależności CustomView sztyletem 2 (w zakresie aktywności)

Tak na przykład, mam LiveData realizacji:

public class CustomLiveData extends LiveData<SomeEvent> { 

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

} 

że chcę wprowadzić do niestandardowego widoku:

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); 
    } 
} 

A oto reszta klas 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{ 
} 

Uwaga: nie otrzymuję żadnych skarg kompilatora, jeśli CustomLiveData jest wstrzykiwany do widoku MainActivity. Dzięki!

+0

Czy pojawił się błąd kompilacji podczas wprowadzania "CustomLiveData" w widoku "Niestandardowy"? Opublikuj go wraz z pytaniem. –

+0

wiadomość została zawarta w części CustomView. Zobacz "// Getting @ com.di.qualifiers.ActivityContext android.content.Context can not ....." – sinek

+0

Czy możesz opublikować "@ ActivityContext"? –

Odpowiedz

13

tl; dr Nie wstrzykiwać zależności warstwy modelu wewnątrz zwyczaju View obiekty

Podklasy View nie są dobrym celem dla Dagger 2 wstrzyknięcia. View obiekty mają być rysowane, a nie muszą, dlatego nazwa "widok". Konstruktorzy dla View powinni to wyjaśnić; są zaprojektowane do nadmuchiwania obiektów z obiektów określonych w XML. Innymi słowy, obiekt View powinien być w stanie być określony w pliku layout.xml, nadmuchany w odpowiednim punkcie cyklu życia, a następnie uzyskany przy użyciu wiązania danych lub danych. W ten sposób najlepsze niestandardowe obiekty View nie mają zależności.

Jeśli chcesz połączyć View i niektóre dane z warstwy modelu, standardowy wzór jest napisać adapter jak i tych, dla RecyclerViewListView. Jeśli nie jest to możliwe, zaleca się użycie narzędzia ustawiającego (np. setData()) do przekazywania zależności z warstwy modelu w konstruktorze lub żądania wykonania iniekcji z jednej z metod cyklu życia produktu View.

Jeśli zamiast tego wstrzykniesz swój obiekt LiveData wewnątrz ćwiczenia lub fragmentu przy użyciu klasy AndroidInjector, zostanie dostarczona poprawna Context bez konieczności wykonywania jakichkolwiek czynności. Wyjaśnia to twój komentarz "Nie otrzymuję żadnych skarg kompilatora, jeśli CustomLiveData jest wstrzykiwany do MainActivity zamiast do widoku."

Po wprowadzeniu obiektu LiveData do tej czynności użyj jednej z powyższych metod (adaptera lub setera) w celu powiązania danych z niestandardowym View.Patrz przykład Google Android Architecture here gdzie elementy z warstwy modelu są wstrzykiwane za pomocą Dagger 2, a następnie wiąże się z ListView użyciu findViewById i setAdapter()

Link do Dagger 2 numerze gdzie wstrzyknięcie View obiektów omówionego:

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

1

hierarchii Dagger wygląda następująco: appcomponent ->activitycomponent

próbować wstrzykiwać aktywność context wewnątrz widok, który zależy od appcomponent bezpośrednio.

Nie jest to możliwe, ponieważ nie istnieje żadna metoda, która mogłaby zapewnić kontekst działania w appcomponent. Zamiast tego, wewnątrz widoku, powinieneś pobrać aktywność (na przykład używając getContext), wyodrębnić z niej activitycomponent i dopiero wtedy wstrzyknąć CustomLiveData.