Что определяет жизненный цикл компонента (графа объектов) в Dagger 2?

Я пытаюсь осмыслить области видимости в 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;
    }
}

Дочерняя область существует до тех пор, пока вы сохраняете на нее ссылку, которая в данном случае была жизненным циклом вашей Activity. Удаление ссылки в onDestroy гарантирует, что граф с областью видимости свободен для сбора мусора.

ИЗМЕНИТЬ

Джесси Уилсон недавно опубликовал mea culpa

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 состоит в том, что для данного компонента аннотированные методы области видимости будут возвращать единственный экземпляр. Я также предполагаю, что Dagger 2 использует аннотацию области для компонента, чтобы убедиться, что модули предоставляют только зависимости, которые находятся либо в той же области, либо без области действия.

В итоге

Зависимости по-прежнему являются одноэлементными или не-одноэлементными, но @Singleton теперь предназначен для одноэлементных экземпляров на уровне приложения, и настраиваемые области являются предпочтительным методом аннотирования одноэлементных зависимостей с более коротким жизненным циклом.

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

Вопрос на $ 64 000 *

Правильно ли я понимаю прицелы и жизненные циклы Dagger 2?

* На самом деле это не вопрос на 64 тысячи долларов.


person Enrico    schedule 09.02.2015    source источник
comment
Вы ничего не пропустили. Управление жизненным циклом каждого компонента осуществляется вручную. По моему собственному опыту, то же самое было и в Dagger 1. При субграфе уровня приложения объект ObjectGraph с использованием plus() ссылки на новый граф сохранялся в Activity и был привязан к его жизненному циклу (разыменовано в onDestroy). Что касается областей видимости, они гарантируют, что реализации ваших компонентов будут сгенерированы без ошибок во время компиляции, и все зависимости будут удовлетворены. Так что это не только для документации. Посмотрите несколько примеров из этой темы.   -  person Kirill Boyarshinov    schedule 10.02.2015
comment
Чтобы прояснить это, методы провайдера с незаданной областью возвращают новые экземпляры при каждой инъекции?   -  person user1923613    schedule 21.07.2015
comment
Почему вы устанавливаете component = null; в onDestroy ()?   -  person Marian Paździoch    schedule 29.04.2016


Ответы (1)


Что касается вашего вопроса

Что определяет жизненный цикл компонента (графа объектов) в 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 для доступа к его собственным зависимостям в области видимости.

  • Компонент может включать только один компонент с заданной областью действия. Зависимости компонентов с несколькими областями видимости не допускаются.

person EpicPandaForce    schedule 26.08.2015
comment
Компонент может включать только один компонент с заданной областью действия. Зависимости компонентов с несколькими областями видимости не допускаются (даже если все они имеют разные области действия, хотя я вроде как думаю, что это ошибка). не совсем понимаю, что это значит - person Damon Yuan; 04.05.2017
comment
Но как насчет livecycle. Будет ли ActivityComponent кандидатом на роль сборщика мусора, если активность будет уничтожена? - person Sever; 21.12.2017
comment
Если вы не храните его где-нибудь еще, тогда да - person EpicPandaForce; 21.12.2017
comment
Поэтому, если нам нужен компонент и внедренный объект, работающий через Activity, мы создаем компонент внутри Activity. Если мы хотим выжить только через фрагмент, я должен создать компонент внутри фрагмента, верно? Где вы храните экземпляр компонента, делает область видимости? - person Thracian; 09.07.2018
comment
Что мне делать, если я хочу, чтобы он выжил благодаря определенной деятельности? - person Thracian; 09.07.2018
comment
@Thracian получили ли вы ответ / опыт на свой вопрос? Я создал области для своих модулей и подкомпонентов с внедренной активностью, но похоже, что один из предоставленных объектов живет за пределами предполагаемого жизненного цикла. - person saintjab; 12.03.2019
comment
Поставщики с ограниченной областью действия разделяют время жизни своего компонента (если, например, вы не передадите его где-нибудь) - person EpicPandaForce; 12.03.2019