Dagger 2'de bir bileşenin (nesne grafiği) yaşam döngüsünü ne belirler?


134

Dagger 2'de, özellikle de kapsamlı grafiklerin yaşam döngüsünde kafamı kapsamları etrafına sarmaya çalışıyorum. Kapsamdan çıktığınızda temizlenecek bir bileşeni nasıl oluşturursunuz?

Bir Android uygulaması söz konusu olduğunda, Dagger 1.x kullanıldığında, genellikle uygulama düzeyinde, etkinlik düzeyinde bir alt kapsam oluşturmak için genişleteceğiniz bir kök kapsamınız olur.

public class MyActivity {

    private ObjectGraph mGraph;

    public void onCreate() {
        mGraph = ((MyApp) getApplicationContext())
            .getObjectGraph()
            .plus(new ActivityModule())
            .inject(this);
    }

    public void onDestroy() {
        mGraph = null;
    }
}

Alt kapsam, bir referans tuttuğunuz sürece mevcuttu, bu durumda Aktivitenizin yaşam döngüsü budur. Referansın onDestroy'a bırakılması, kapsamlı grafiğin çöp toplama işleminin serbest olmasını sağladı.

DÜZENLE

Jesse Wilson kısa süre önce bir mea culpa yayınladı

Dagger 1.0, kapsam adlarını kötü bir şekilde mahvetti ... @Singleton ek açıklaması hem kök grafikler hem de özel grafikler için kullanılır, bu nedenle bir şeyin gerçek kapsamının ne olduğunu anlamak zordur.

ve okuduğum / duyduğum diğer her şey, kapsamların çalışma şeklini iyileştiren Dagger 2'ye doğru işaret ediyor, ancak farkı anlamakta zorlanıyorum. @Kirill Boyarshinov'un aşağıdaki yorumuna göre, bir bileşenin yaşam döngüsü veya bağımlılık her zamanki gibi somut referanslarla belirleniyor. Öyleyse, Dagger 1.x ve 2.0 kapsamları arasındaki fark, tamamen anlamsal bir açıklık meselesi mi?

Benim anlayışım

Hançer 1.x

Bağımlılıklar ya @Singletonda değildi. Bu, kök grafik ve alt grafiklerdeki bağımlılıklar için de aynı derecede geçerliydi ve bağımlılığın hangi grafiğe bağlı olduğu konusunda belirsizliğe yol açtı (bkz . inşa edildi mi? )

Hançer 2.0

Özel kapsamlar, anlamsal olarak net kapsamlar oluşturmanıza izin verir, ancak işlevsel @Singletonolarak Dagger 1.x'te uygulamaya eşdeğerdir .

// Application level
@Singleton
@Component( modules = MyAppModule.class )
public interface MyAppComponent {
    void inject(Application app);
}

@Module
public class MyAppModule {

    @Singleton @Named("SingletonScope") @Provides
    StringBuilder provideStringBuilderSingletonScope() {
        return new StringBuilder("App");
    }
}

// Our custom scope
@Scope public @interface PerActivity {}

// Activity level
@PerActivty
@Component(
    dependencies = MyAppComponent.class,
    modules = MyActivityModule.class
)
public interface MyActivityComponent {
    void inject(Activity activity);
}

@Module
public class MyActivityModule {

    @PerActivity @Named("ActivityScope") @Provides
    StringBuilder provideStringBuilderActivityScope() {
        return new StringBuilder("Activity");
    }

    @Name("Unscoped") @Provides
    StringBuilder provideStringBuilderUnscoped() {
        return new StringBuilder("Unscoped");
    }
}

// Finally, a sample Activity which gets injected
public class MyActivity {

    private MyActivityComponent component;

    @Inject @Named("AppScope")
    StringBuilder appScope

    @Inject @Named("ActivityScope")
    StringBuilder activityScope1

    @Inject @Named("ActivityScope")
    StringBuilder activityScope2

    @Inject @Named("Unscoped")
    StringBuilder unscoped1

    @Inject @Named("Unscoped")
    StringBuilder unscoped2

    public void onCreate() {
        component = Dagger_MyActivityComponent.builder()
            .myApplicationComponent(App.getComponent())
            .build()
            .inject(this);

        appScope.append(" > Activity")
        appScope.build() // output matches "App (> Activity)+" 

        activityScope1.append("123")
        activityScope1.build() // output: "Activity123"

        activityScope2.append("456")
        activityScope1.build() // output: "Activity123456"

        unscoped1.append("123")
        unscoped1.build() // output: "Unscoped123"

        unscoped2.append("456")
        unscoped2.build() // output: "Unscoped456"

    }

    public void onDestroy() {
        component = null;
    }

}

Paket , kullanımın bu bileşenin yaşam döngüsü ile ilgili niyetinizi@PerActivity iletmesidir , ancak nihayetinde bileşeni her yerde / her zaman kullanabilirsiniz. Dagger'ın tek sözü, belirli bir bileşen için, kapsam açıklamalı yöntemlerin tek bir örnek döndürecektir. Ayrıca, Dagger 2'nin, modüllerin yalnızca aynı kapsamda olan veya kapsam dışı bağımlılıkları sağladığını doğrulamak için bileşendeki kapsam açıklamasını kullandığını varsayıyorum.

Özetle

Bağımlılıklar hala tekildir veya tekil değildir, ancak @Singletonartık uygulama düzeyinde tekli örnekler için tasarlanmıştır ve özel kapsamlar, daha kısa bir yaşam döngüsüyle tekli bağımlılıklara açıklama eklemek için tercih edilen yöntemdir.

Geliştirici, artık gerekli olmayan referansları atarak bileşenlerin / bağımlılıkların yaşam döngüsünü yönetmekten sorumludur ve bileşenlerin amaçlandıkları kapsamda yalnızca bir kez oluşturulmasını sağlamaktan sorumludur, ancak özel kapsam ek açıklamaları bu kapsamın tanımlanmasını kolaylaştırır. .

64.000 Dolarlık Soru *

Dagger 2 kapsamları ve yaşam döngüleri hakkındaki anlayışım doğru mu?

* Aslında 64.000 dolarlık bir soru değil.


5
Hiçbir şeyi kaçırmadın. Her bileşenin yaşam döngüsünü yönetmek manueldir. Benim deneyimlerime göre aynısı Hançer 1'de de geçerliydi. Uygulama düzeyinde ObjectGraph nesnesi alt grafiğe aktarılırken plus(), yeni grafiğe referans kullanılarak Activity'de saklanıyordu ve yaşam döngüsüne bağlıydı (referansı kaldırıldı onDestroy). Kapsamlara gelince, bileşen uygulamalarınızın derleme zamanında hatasız ve her bağımlılık karşılanarak üretilmesini sağlarlar. Yani sadece dokümantasyon amaçlı değil. Bu konudan bazı örneklere göz atın .
Kirill Boyarshinov

1
Daha açık olmak gerekirse, "kapsamı kaldırılmış" sağlayıcı yöntemleri her enjeksiyonda yeni örnekler döndürüyor mu?
user1923613

2
Neden component = null; onDestroy () içinde?
Marian Paździoch

Yanıtlar:


70

Sorunuza gelince

Dagger 2'de bir bileşenin (nesne grafiği) yaşam döngüsünü ne belirler?

Kısa cevap, onu siz belirlersiniz . Bileşenlerinize aşağıdaki gibi bir kapsam verilebilir:

@Scope
@Retention(RetentionPolicy.RUNTIME)
public @interface ApplicationScope {
}

@Scope
@Retention(RetentionPolicy.RUNTIME)
public @interface ActivityScope {
}

Bunlar sizin için iki şey için faydalıdır:

  • Kapsamın doğrulanması: bir bileşenin yalnızca kapsamı kaldırılmış sağlayıcıları veya bileşeninizle aynı kapsamın kapsamı belirlenmiş sağlayıcıları olabilir.

.

@Component(modules={ApplicationModule.class})
@ApplicationScope
public interface ApplicationComponent {
    Something something();
    AnotherThing anotherThing();

    void inject(Whatever whatever);
}

@Module
public class ApplicationModule {
    @ApplicationScope //application-scoped provider, only one can exist per component
    @Provides
    public Something something() {
         return new Something();
    }

    @Provides //unscoped, each INJECT call creates a new instance
    public AnotherThing anotherThing() {
        return new AnotherThing();
    }
}
  • Kapsamlı bağımlılıklarınızın alt kapsamını belirlemenize olanak tanır, böylece "üst kapsamlı" bileşenden sağlanan örnekleri kullanan "alt kapsama alınmış" bir bileşen oluşturmanıza olanak tanır.

Bu, @Subcomponentaçıklama veya bileşen bağımlılıkları ile yapılabilir . Şahsen bağımlılıkları tercih ederim.

@Component(modules={ApplicationModule.class})
@ApplicationScope
public interface ApplicationComponent {
    Something something();
    AnotherThing anotherThing();

    void inject(Whatever whatever);

    ActivityComponent newActivityComponent(ActivityModule activityModule); //subcomponent factory method
}

@Subcomponent(modules={ActivityModule.class})
@ActivityScope
public interface ActivityComponent {
    ThirdThingy thirdThingy();

    void inject(SomeActivity someActivity);
}

@Module
public class ActivityModule {
    private Activity activity;

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

    //...
}

ApplicationComponent applicationComponent = DaggerApplicationComponent.create();
ActivityComponent activityComponent = applicationComponent.newActivityComponent(new ActivityModule(SomeActivity.this));

Veya bileşen bağımlılıklarını şu şekilde kullanabilirsiniz:

@Component(modules={ApplicationModule.class})
@ApplicationScope
public class ApplicationComponent {
    Something something(); 
    AnotherThing anotherThing();

    void inject(Whatever whatever);
}

@Component(dependencies={ApplicationComponent.class}, modules={ActivityModule.class})
@ActivityScope
public interface ActivityComponent extends ApplicationComponent {
    ThirdThingy thirdThingy();

    void inject(SomeActivity someActivity);
}

@Module
public class ActivityModule {
    private Activity activity;

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

    //...
}

ApplicationComponent applicationComponent = DaggerApplicationComponent.create();
ActivityComponent activityComponent = DaggerActivityComponent.builder().activityModule(new ActivityModule(SomeActivity.this)).build();

Bilmeniz gereken önemli şeyler:

  • Kapsamlı bir sağlayıcı, her bileşen için verilen kapsam için bir örnek oluşturur . Bir bileşenin kendi örneklerini takip etmesi, ancak diğer bileşenlerin paylaşılan bir kapsam havuzuna veya biraz sihre sahip olmadığı anlamına gelir. Belirli bir kapsamda bir örneğe sahip olmak için bileşenin bir örneğine ihtiyacınız vardır. Bu nedenle ApplicationComponent, kendi kapsamlı bağımlılıklarına erişim sağlamanız gerekir .

  • Bir bileşen yalnızca bir kapsamlı bileşeni kapsayabilir. Birden çok kapsamlı bileşen bağımlılığına izin verilmez.


Bir bileşen yalnızca bir kapsamlı bileşeni kapsayabilir. Birden çok kapsamlı bileşen bağımlılığına izin verilmiyor (hepsinin farklı kapsamları olsa bile, bunun bir hata olduğunu düşünüyorum). tam olarak ne anlama geldiğini anlamıyorum
Damon Yuan

Peki ya yaşam döngüsü? Etkinlik bozulursa, ActivityComponent çöp toplayıcı adayı olur mu?
Sever

Başka bir yerde
saklamıyorsanız,

1
Yani, bileşene ve enjekte edilen nesneye Aktivite yoluyla canlı olarak ihtiyacımız varsa, bileşeni Activity içinde oluştururuz. Sadece bir Fragman aracılığıyla hayatta kalmak istiyorsak, parçanın içinde bileşen oluşturmalıyım, değil mi? Bileşen örneğini nerede tuttuğunuz kapsam yapar?
Trakya

Belirli bir Aktivite ile hayatta kalmasını istiyorsam ne yapmalıyım?
Trakya
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.