Apa yang menentukan siklus hidup komponen (objek grafik) di Belati 2?

134

Saya mencoba untuk membungkus kepala saya di sekitar lingkup di Dagger 2, khususnya siklus hidup dari grafik yang dilingkari. Bagaimana Anda membuat komponen yang akan dibersihkan ketika Anda meninggalkan ruang lingkup.

Dalam hal aplikasi Android, menggunakan Belati 1.x Anda umumnya memiliki ruang lingkup root di tingkat aplikasi yang Anda akan memperluas untuk membuat ruang lingkup anak di tingkat aktivitas.

public class MyActivity {

    private ObjectGraph mGraph;

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

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

Lingkup anak ada selama Anda menyimpan referensi untuknya, yang dalam hal ini adalah siklus hidup Aktivitas Anda. Menjatuhkan referensi di onDestroy memastikan grafik yang dicakup bebas dari sampah yang dikumpulkan.

EDIT

Jesse Wilson baru-baru ini memposting cula mea

Belati 1.0 benar-benar mengacaukan nama ruang lingkupnya ... Anotasi @Singleton digunakan untuk grafik akar dan grafik khusus, jadi sulit untuk mengetahui apa sebenarnya ruang lingkup suatu benda.

dan segala sesuatu yang telah saya baca / dengar poin menuju Dagger 2 meningkatkan cara kerja lingkup, tapi saya berjuang untuk memahami perbedaannya. Menurut komentar @ Kirill Boyarshinov di bawah ini, siklus hidup komponen atau ketergantungan masih ditentukan, seperti biasa, dengan referensi konkret. Jadi, apakah perbedaan antara cakupan Dagger 1.x dan 2.0 hanya masalah kejelasan semantik?

Pemahaman saya

Belati 1.x

Dependensi ada @Singletonatau tidak. Ini sama benarnya dengan dependensi dalam grafik root dan subgraf, yang mengarah ke ambiguitas terkait dengan grafik yang mana ketergantungan itu terikat (lihat Dalam Belati adalah Singleton dalam sub-grafik yang di-cache atau akan selalu diciptakan kembali ketika suatu sub-grafik aktivitas baru dibangun? )

Belati 2.0

Lingkup khusus memungkinkan Anda untuk membuat ruang lingkup yang jelas secara semantik, tetapi secara fungsional setara dengan penerapan @Singletondi Belati 1.x.

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

}

Hal yang dapat diambil adalah bahwa penggunaan @PerActivitymengkomunikasikan niat Anda mengenai siklus hidup komponen ini, tetapi pada akhirnya Anda dapat menggunakan komponen di mana saja / kapan saja. Satu-satunya janji belati adalah bahwa, untuk komponen tertentu, metode yang dijelaskan lingkup akan mengembalikan satu contoh. Saya juga menganggap Dagger 2 menggunakan penjelasan lingkup pada komponen untuk memverifikasi bahwa modul hanya menyediakan dependensi yang berada dalam cakupan yang sama atau non-cakupan.

Singkatnya

Dependensi masih berupa singleton atau non-singleton, tetapi @Singletonsekarang ditujukan untuk instance singleton level aplikasi dan cakupan kustom adalah metode yang disukai untuk menjelaskan ketergantungan singleton dengan siklus hidup yang lebih pendek.

Pengembang bertanggung jawab untuk mengelola siklus hidup komponen / dependensi dengan menjatuhkan referensi yang tidak lagi diperlukan dan bertanggung jawab untuk memastikan bahwa komponen hanya dibuat satu kali dalam ruang lingkup yang dimaksudkan, tetapi penjelasan ruang lingkup kustom memudahkan untuk mengidentifikasi ruang lingkup itu. .

Pertanyaan $ 64rb *

Apakah pemahaman saya tentang lingkup dan siklus hidup Dagger 2 benar?

* Sebenarnya bukan pertanyaan $ 64'000.

Enrico
sumber
5
Anda tidak melewatkan apa pun. Mengelola siklus hidup setiap komponen adalah manual. Dari pengalaman saya sendiri, hal yang sama juga terjadi pada Belati 1. Ketika subgraphing level aplikasi objek ObjectGraph menggunakan plus()referensi ke grafik baru disimpan di Activity, dan terikat pada siklus hidupnya (dereferenced in onDestroy). Adapun cakupan, mereka memastikan implementasi komponen Anda dihasilkan tanpa kesalahan pada waktu kompilasi, dengan setiap ketergantungan terpenuhi. Jadi bukan hanya untuk keperluan dokumentasi. Lihatlah beberapa contoh dari utas ini .
Kirill Boyarshinov
1
Untuk memperjelas hal ini, metode penyedia "yang tidak terentang" mengembalikan contoh baru pada setiap suntikan?
user1923613
2
Mengapa Anda mengatur komponen = null; di onDestroy ()?
Marian Paździoch

Jawaban:

70

Adapun pertanyaan Anda

Apa yang menentukan siklus hidup komponen (objek grafik) di Belati 2?

Jawaban singkatnya adalah Anda menentukannya . Komponen Anda dapat diberi ruang lingkup, seperti

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

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

Ini berguna bagi Anda untuk dua hal:

  • Validasi ruang lingkup: komponen hanya dapat memiliki penyedia yang tidak dicopot, atau penyedia lingkup dengan cakupan yang sama dengan komponen Anda.

.

@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();
    }
}
  • Memungkinkan sub-pelingkupan dependensi cakupan Anda, sehingga memungkinkan Anda untuk membuat komponen "subscoped" yang menggunakan instance yang disediakan dari komponen "superscoped".

Ini dapat dilakukan dengan @Subcomponentanotasi, atau dependensi komponen. Saya pribadi lebih suka ketergantungan.

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

Atau Anda dapat menggunakan dependensi komponen seperti itu

@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();

Hal-hal penting untuk diketahui:

  • Penyedia scoped menciptakan satu contoh untuk lingkup yang diberikan untuk setiap komponen . Berarti komponen melacak instance-nya sendiri, tetapi komponen lain tidak memiliki kumpulan lingkup bersama atau sihir. Untuk memiliki satu instance dalam ruang lingkup yang diberikan, Anda memerlukan satu instance komponen. Inilah sebabnya mengapa Anda harus menyediakan ApplicationComponentuntuk mengakses dependensi cakupannya sendiri.

  • Suatu komponen dapat hanya memiliki satu komponen yang dibatasi. Beberapa dependensi komponen cakupan tidak diizinkan.

EpicPandaForce
sumber
Suatu komponen dapat hanya memiliki satu komponen yang dibatasi. Beberapa dependensi komponen cakupan tidak diizinkan (bahkan jika mereka semua memiliki cakupan yang berbeda, meskipun saya agak berpikir itu bug). tidak benar-benar mengerti apa artinya
Damon Yuan
Tapi bagaimana dengan livecycle. Akankah kandidat ActivityComponent untuk pemungut sampah jika aktivitasnya dihancurkan?
Sever
Jika Anda tidak menyimpannya di tempat lain maka ya
EpicPandaForce
1
Jadi jika kita memerlukan komponen dan objek yang disuntikkan secara langsung melalui Activity, kita membangun komponen di dalam Activity. Jika kita hanya ingin bertahan hidup melalui Fragmen saya harus membangun komponen di dalam fragmen, kan? Di mana Anda menyimpan instance komponen membuat ruang lingkup?
Thracian
Apa yang harus saya lakukan jika saya ingin tetap hidup melalui Kegiatan tertentu?
Thracian