Dagger 2 scope i subcomponents

Staram się, aby moja aplikacja była lepsza i Kod był łatwiejszy do utrzymania za pomocą Dagger2 wpadłem na ogólny pomysł, ale nadal nie mogę dowiedzieć się, jak zakresy są zarządzane przez Dagger2 Wstrzyknąłem sztylet do mojego projektu(brzmi zabawnie). Stworzyłem komponent ApplicationComonent i sprawdza się on doskonale w moim projekcie. Oto Mój kod.

@Singleton
@Component(modules = {
        ApplicationModule.class,
        ThreadingModule.class,
        NetworkModule.class,
        DatabaseModule.class,
        ServiceModule.class,
        ParseModule.class,
        PreferencesSessionModule.class})

public interface ApplicationComponent {
    ActivityComponent activityComponent(ActivityModule activityModule);

    void inject(BaseActivity baseActivity);

    void inject(MainAppActivity mainAppActivity);

    void inject(MyApplication application);

    void inject(BaseFragment baseFragment);

    void inject(MyService service);

    void inject(RegistrationIntentService service);
}

Tworzę swoją instancję komponentu w klasie MyApplication w ten sposób

private void initializeAndInjectComponent() {
        mApplicationComponent =
                DaggerApplicationComponent
                        .builder()
                        .threadingModule(new ThreadingModule(1))
                        .applicationModule(new ApplicationModule(this))
                        .networkModule(new NetworkModule(
                                MyService.API_SERVER_BASE_URL,
                                MyService.TIMEOUT))
                        .build();
        mApplicationComponent.inject(this);
    }

I mogę uzyskać składnik w celu wstrzyknięcia w moim Activities

    MyApplication application = MyApplication.get(this);
    application.getApplicationComponent().inject(this);

Wszystko działa doskonale.

Aby dodać każdą metodę, a także klasę modułu jest adnotowana za pomocą @Singleton scope, wszystkie moduły związane z ApplicationComponent

Teraz chcę ulepszyć zależności I widziałem wiele przykładów z niestandardowymi zakresami, takimi jak @PerActivity, @PerFragment. Mam wiele pytań, ale o to później.

Więc stworzyłem ActivityComponent

@PerActivity
@Subcomponent(
        modules = {
                NetworkServiceModule.class,
                ActivityModule.class,
                PermissionModule.class
        })
public interface ActivityComponent {
    Activity activity();

    void inject(BaseActivity baseActivity);
}

Wszystkie moduły wyglądają tak

@PerActivity
@Module
public class ActivityModule {
    private Activity mActivity;

    public ActivityModule(Activity activity) {
        this.mActivity = activity;
    }

    @Provides
    @PerActivity
    Activity provideActivity() {
        return this.mActivity;
    }
}

Mam następujące zależności w moim BaseActivity

// Dependencies from ApplicationComponent
    @Inject
    protected ApplicationSettingsManager mApplicationSettingsManager;
    @Inject
    protected ScheduledThreadPoolExecutor mPoolExecutor;
// Dependencies from ActivityComponent
    @Inject
    protected SpiceManager mSpiceManager;
    @Inject
    protected PermissionController mPermissionController;

I w mojej onCreate() metodzie Wstrzykuję w następujący sposób

    MyApplication application = MyApplication.get(this);
    application.getApplicationComponent().activityComponent(new ActivityModule(this)).inject(this);

Przed utworzeniem subkomponentu ActivityComponent było to

   MyApplication application = MyApplication.get(this);
        application.getApplicationComponent().inject(this);

Teraz mam błąd

Error:(34, 10) error: com.octo.android.robospice.SpiceManager cannot be provided without an @Inject constructor or from an @Provides- or @Produces-annotated method.
BaseActivity.mSpiceManager
[injected field of type: com.octo.android.robospice.SpiceManager mSpiceManager]
Nie wiem, gdzie jest problem, co przegapiłem. Moje pytania dotyczące lunet w dagger2.

Wszystko oprócz @Singleton jest ignorowane przez Dagger 2, mam rację ? Nie rozumiem, jak zarządza się życiem komponentu ? Mam tylko jeden pomysł

  1. Gdy używasz @Singleton adnotacja dagger tworzy obiekt w jakiejś puli statycznej które będą istniały podczas całego cyklu życia aplikacji i zostaną zniszczone, gdy instancja JVM (dalvik VM, ART)zostanie zniszczona.

  2. Gdy używasz innych adnotacji, tylko dla Ciebie jako programisty, aby lepiej zachować kod, @PerActivity, @PerFragment to tylko niestandardowe adnotacje nic więcej. I jeśli umieścisz @PerFragment komponent w klasie aplikacji, będzie on żył tak długo, jak długo będzie żył. Mam rację ?

  3. Więc rozumiem to w ten sposób, jeśli dagger znajdzie @Singleton adnotacja dodaje statyczne odniesienie do komponentu, gdy jest tworzony po raz pierwszy, a w przypadku jakiejkolwiek innej adnotacji nie zawiera odniesienia do komponentu.

Byłbym bardzo wdzięczny za każdą pomoc w problemach opisanych powyżej.

UPDATE

Dziękuję David Medenjak za świetną odpowiedź, mam dużo lepsze zrozumienie Dagger2.

Właśnie znalazłem problem, o ile używam osobnego komponentu Activity, zapomniałem o dwie linie w ApplicationComponent i zmiana inejction w moim MainActivity na ActivityComponent zamiast ApplicationComponent, więc na pewno nie może rozwiązać zależności od subkomponentu.

 void inject(BaseActivity baseActivity);

 void inject(MainAppActivity mainAppActivity);

Teraz wszystko działa idealnie, Lubię {[10] } i oddzielną architekturę.

Author: CROSP, 2016-04-02

1 answers

Trochę radykalne, ale dla uproszczenia rzeczy: wszystkie adnotacje dotyczące zakresu są niczym innym jak cukrem składniowym-w tym @Singleton.

Zakresy najczęściej tylko sprawdzają czas kompilacji. Cykliczne zależności lub błędy dotyczące rzeczy, które mogły zostać pominięte. @Singleton jest jak każdy inny zakres, jedyną różnicą jest to, że jest to już istniejąca adnotacja i nie musisz jej tworzyć samodzielnie. Możesz użyć @MySingleton zamiast tego.

[...] dagger tworzy obiekt w część puli statycznej, która będzie istnieć podczas całego cyklu życia aplikacji

Nie. Dagger nie robi nic statycznego. Masz obiekty komponentowe. Komponenty te przechowują obiekty utworzone przez moduły. Jeśli obiekt w komponencie ma zakres komponentu, zostanie utworzony tylko raz w tego dokładnego komponentu. Jeśli zdecydujesz się utworzyć 2 Obiekty AppComponent, będziesz miał 2 obiekty z każdego @Singleton z adnotacją, każdy w jego składniku. Dlatego powinieneś zachowaj odniesienie do komponentu. Większość implementacji, które widziałem lub używałem, przechowuje AppComponent w swoim Application. Jeśli to zrobisz, możesz go użyć Jak singleton-to wciąż tylko POJO.

[...] umieszczasz komponent @PerFragment w klasie aplikacji, który będzie żył tak długo, jak długo będzie żył.

Tak. Jak już wspomniano powyżej, jest to tylko przedmiot. Zachowaj referencje, zachowaj obiekty. Wyrzucić lub utwórz nowy i masz nowe obiekty (zdefiniowane w tym komponencie / zakresie). Chociaż nie należy przechowywać komponenty o zasięgu aktywności lub fragmentu w dowolnym miejscu poza odpowiednio aktywnościami lub fragmentami, ponieważ przechowywanie ich np. w komponencie aplikacji najprawdopodobniej doprowadzi do wycieku pamięci. (Jeśli nie, prawdopodobnie nie potrzebowałbyś zakresu aktywności lub fragmentu.)

Jeśli dagger znajdzie @Singleton adnotację, doda statyczne odniesienie do komponentu, gdy jest ona tworzona po raz pierwszy i w przypadku innych adnotacji nie będzie zawierała odniesienia do komponentu.

Znowu nie. Nic statycznego. Zwykłe stare obiekty Javy. Możesz mieć wiele @Singleton komponentów z własnymi obiektami, ale prawdopodobnie nie powinieneś (chociaż to sprawia, że testowanie oprzyrządowania jest możliwe / łatwe-po prostu zamień komponenty.)

Twój wspomniany błąd

SpiceManager nie może być dostarczony bez konstruktora @ Inject lub z @Provides - or @ Produces-adnotated method.

Oznacza to, że komponent, którym próbujesz wstrzyknąć obiekt, nie może znaleźć sposobu na wyprodukowanie lub dostarczenie SpiceManager. Upewnij się, że dostarczasz go z AppComponent lub innego miejsca, nie brakuje żadnych adnotacji itp.

 58
Author: David Medenjak,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-04-02 11:48:42