Що визначає життєвий цикл компонента (об’єктного графіка) у Dagger 2?


134

Я намагаюся обернути голову навколо областей в Dagger 2, зокрема, життєвого циклу нанесених графіків. Як створити компонент, який буде очищений, коли ви вийдете з області застосування.

У випадку з додатком для Android, використовуючи Dagger 1.x, у вас зазвичай є коренева область на рівні програми, яку ви розширите, щоб створити дочірнє поле на рівні активності.

public class MyActivity {

    private ObjectGraph mGraph;

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

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

Дітям сфера існувала до тих пір, поки ви не посилалися на неї, що в цьому випадку було життєвим циклом вашої діяльності. Якщо ввімкнути посилання на onDestroy, гарантія того, що графік, на якому було зафіксовано, можна було збирати сміття

EDIT

Нещодавно Джессі Вілсон опублікував мій випадок

Dagger 1.0 погано розкрутив свої назви областей ... Анотація @Singleton використовується як для кореневих графіків, так і для користувацьких графіків, тому складно розібратися, що таке фактична сфера речей.

і все інше, що я прочитав / почув, вказує на Dagger 2, що покращує спосіб роботи областей, але я намагаюся зрозуміти різницю. Відповідно до коментаря @Kirill Boyarshinov нижче, життєвий цикл компонента або залежності все ще визначається, як зазвичай, конкретними посиланнями. Тож чи різниця між діапазонами Dagger 1.x та 2.0 є суто семантичною ясністю?

Моє розуміння

Кинджал 1.x

Залежності були @Singletonчи ні. Це однаковою мірою стосувалося залежностей у кореневому графіку та підграфах, що призводило до неоднозначності щодо того, до якого графа залежність залежала (див. У Dagger - синглтони у кешованому під графіку, або вони завжди будуть відтворені, коли новий під графік активності будується? )

Кинджал 2.0

Спеціальні області застосування дозволяють створювати семантично чіткі області застосування, але вони функціонально еквівалентні застосуванню @Singletonв Dagger 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;
    }

}

Це означає, що при використанні @PerActivityповідомляється про ваш намір щодо життєвого циклу цього компонента, але в кінцевому підсумку ви можете використовувати його в будь-якому місці та в будь-який час. Єдине обіцянка Дагера полягає в тому, що для даного компонента методи, що коментують область, повернуть один екземпляр. Я також припускаю, що Dagger 2 використовує анотацію про область використання компонента для перевірки того, що модулі забезпечують лише залежності, які знаходяться або в тому самому масштабі, або не входять в рамки.

Підсумок

Залежності як і раніше є однотонними або неоднотонними, але @Singletonтепер призначені для прикладних одиночних екземплярів на рівні застосунку, а користувацькі сфери є кращим методом для анотування залежностей від однотонних з меншим життєвим циклом.

Розробник несе відповідальність за управління життєвим циклом компонентів / залежностей шляхом викидання посилань, які більше не потрібні, і відповідальних за забезпечення того, щоб компоненти створювалися лише один раз у тій області, для якої вони призначені, але користувацькі анотації щодо сфери полегшують ідентифікацію цієї сфери .

Питання $ 64k *

Чи правильно я розумію сфери дії та життєвий цикл Dagger 2?

* Насправді не питання на $ 64'000.


5
Ти нічого не пропустив. Керування живим циклом кожного компонента є ручним. З мого власного досвіду те саме було і в Dagger 1. При підрозділі рівня програми Об'єкт ObjectGraph, використовуючи plus()посилання на новий графік, зберігався в "Діяльності" і прив'язувався до його "живого циклу" (відмінено в onDestroy). Що стосується діапазонів, вони гарантують, що ваші компоненти компонентів генеруються без помилок під час компіляції, з задоволенням кожної залежності. Тож це не лише з метою документації. Ознайомтеся з прикладом цієї теми .
Кирило Бояршинов

1
Просто для того, щоб зрозуміти це, "необдумані" методи постачальника повертають нові екземпляри на кожну ін'єкцію?
user1923613

2
Чому ви встановлюєте компонент = null; в onDestroy ()?
Marian Paździoch

Відповіді:


70

Щодо вашого питання

Що визначає життєвий цикл компонента (об’єктного графіка) у Dagger 2?

Коротка відповідь - це ти визначиш . Вашим компонентам може бути надано сферу застосування, наприклад

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

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

Ці корисні для вас дві речі:

  • Перевірка сфери застосування: компонент може мати лише неспецифікованих постачальників або обширних постачальників такого ж обсягу, що і ваш компонент.

.

@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();
    }
}
  • Дозволяє здійснити підрозділ ваших залежностей, таким чином дозволяючи створити "субскопічний" компонент, який використовує надані екземпляри з "суперскопічного" компонента.

Це можна зробити за допомогою @Subcomponentанотацій або залежностей від компонентів. Я особисто віддаю перевагу залежності.

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

Або ви можете використовувати компоненти компонентів, як це

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

Важливі речі:

  • Провайдер, що охоплює масштаб, створює один екземпляр для даної області для кожного компонента . Це означає, що компонент відслідковує власні екземпляри, але інші компоненти не мають спільного пулу областей чи якоїсь магії. Щоб мати один екземпляр у заданій області, вам потрібен один примірник компонента. Ось чому ви повинні забезпечити ApplicationComponentдоступ до власних залежностей, що їх визначають.

  • Компонент може субскопувати лише один компонент, який охоплює область. Забороненість залежно від декількох компонентів заборонена.


Компонент може субскопувати лише один компонент, який охоплює область. Забороненість декількох компонентів, які залежать від рівня, не допускаються (навіть якщо всі вони мають різні сфери, хоча я начебто думаю, що це помилка). не дуже розумію, що це означає
Деймон Юань

А як же живий цикл. Чи буде кандидатом ActivityComponent збирач сміття, якщо діяльність знищена?
Север

Якщо ви не зберігаєте його деінде, то так
EpicPandaForce

1
Таким чином, якщо нам потрібен компонент та об'єкт, що вводиться в реальному часі через Activity, ми будуємо компонент всередині Activity. Якщо ми хочемо вижити лише через фрагмент, я повинен створити компонент всередині фрагмента, правда? Де ви зберігаєте, щоб екземпляр компонента створює область?
фракійський

Що мені робити, якщо я хочу, щоб він вижив завдяки певній діяльності?
фракійський
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.