Цикл включения модуля «Кинжал»

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

Например, предположим, что у меня есть основной модуль приложения, определенный следующим образом:

//com.example.android.MyAppModule.java
@Module(
   includes = AnalyticsModule.class,
   injects = { <snip> }
)
public class MyAppModule {
   // various provides
}

И у меня есть еще один модуль, определенный подобным образом, который настраивает интерфейс ErrorReporter и предоставляет ему конкретную реализацию.

// com.example.android.analytics.AnalyticsModule.java
@Module(
   addsTo = MyAppModule.class,
   injects = { MyApp.class }
)
public class AnalyticsModule(){

    // ErrorReporter is a public interface and ErrorReporterImpl is a package-local final concrete class that implements it
    @Provides @Singleton
    ErrorReporter providesErrorReporter(ErrorReporterImpl reporter) { return reporter };
}

В моем классе Application я настроил граф объектов следующим образом:

// com.example.android.MyApp.java
public class MyApp extends Application {
    @Inject ErrorReporter errorReporter;

    @Override
    public void onCreate() {
        super.onCreate();
        applicationGraph = ObjectGraph
            .create(new MyAppModule())
            .plus(new AnalyticsModule());
        applicationGraph.inject(this);
        errorReporter.initialize();
    }
}

Когда я запускаю компилятор dagger, я получаю что-то вроде этого:

Graph validation failed: Module Inclusion Cycle:
0. com.example.android.analytics.AnalyticsModule included by com.example.android.MyAppModule
1. com.example.android.modules.MyAppModule included by com.example.android.analytics.AnalyticsModule
0. com.example.android.analytics.AnalyticsModule

Что я здесь делаю неправильно? Я предполагаю, что это как-то связано с include/addsTo, но когда я удаляю их, я получаю другие ошибки.

Если я удалю include = AnalyticsModule.class из MyAppModule, я получу что-то вроде этого:

com.example.android.analytics.ErrorReporter could not be bound with key com.example.android.analytics.ErrorReporter required by com.example.android.MyApp for com.example.android.MyAppModule

Все в порядке, если я полностью откажусь от AnalyticsModule, а затем передам ProvideErrorReporter MyAppModule, но тогда мне придется сделать свой конкретный класс реализации общедоступным, чтобы я мог использовать его в другом модуле.


person Paul    schedule 03.02.2014    source источник
comment
Возможно ли, что у вас есть строка типа includes = MyAppModule.class в аннотации к AnalyticsModule? Потому что это сформировало бы цикл. Также plus(new AnalyticsModule()) не нужен, includes = AnalyticsModule.class сделает это за вас.   -  person Tavian Barnes    schedule 05.02.2014
comment
Нет, только addTo. Я приступил к работе, если удалил addTo, а затем пометил AnalyticsModule complete = false... но я чувствую, что это делает так, что я могу выстрелить себе в ногу.   -  person Paul    schedule 05.02.2014
comment
О да, это имеет смысл.   -  person Tavian Barnes    schedule 05.02.2014


Ответы (2)


addsTo= указывает, что этот граф является расширением указанного модуля в родительском/дочернем графе. .plus() - это версия во время выполнения. Вам нужен .plus() только в том случае, если у вас есть более короткие экземпляры, управляемые графом. Это примерно соответствует понятию «область действия» в Guice и других контейнерах внедрения зависимостей.

В вашем примере вы делаете:

applicationGraph = ObjectGraph
    .create(new MyAppModule())
    .plus(new AnalyticsModule());

Это приводит к созданию двух графиков в отношениях родитель/потомок, что вам здесь не нужно. В приложении для Android вам нужен один график для приложения.

Вы можете просто удалить addTo, и MyAppModule автоматически создаст экземпляр AnalyticsModule, или вы можете передать оба, если хотите избежать динамической инициализации следующим образом:

applicationGraph = ObjectGraph.create(new MyAppModule(), new AnalyticsModule());

Цикл включения модулей связан с тем, что у вас есть циклические отношения между этими двумя модулями, а сами модули должны формировать ациклический граф конфигурации. MyAppModule включает AnalyticsModule, который, в свою очередь, включает MyAppModule. (addsTo — менее строгое include=, используемое для указания вещей, полученных из родительских графов)

person Christian Gruber    schedule 20.02.2014

@Module(includes = AnalyticsModule.class) полезен для объединения нескольких модулей в один. В этом случае использование .plus(new AnalyticsModule()) неверно, поскольку AnalyticsModule уже было включено строкой includes в аннотацию. Поэтому вам следует удалить вызов plus().

@Module(addsTo = MyAppModule.class) для разрешения вызова .plus(new AnalyticsModule()). Поскольку мы удалили этот вызов, мы также должны удалить addsTo.

@Module(complete = false) необходим для AnalyticsModule, потому что некоторые из его зависимостей не могут быть удовлетворены сами по себе. Это нормально; пока MyAppModule имеет complete = true (по умолчанию), Dagger будет выполнять необходимую проверку ошибок.

Правда, ошибка «Цикл включения модуля» была немного неясной. Цикл был вызван тем, что А включает В, которое добавляется к А.

person Tavian Barnes    schedule 04.02.2014
comment
Потрясающе, спасибо за объяснение. Есть ли какая-либо документация о различных входных данных для аннотации @Module? Я обнаружил, что сам код хорошо прокомментирован, но было трудно найти примеры/учебники о том, где вы хотели бы использовать различные клавиши для разных целей. - person Paul; 05.02.2014
comment
Официальной документации о таких вещах не так уж и много. У них есть несколько примеров на github и краткое руководство на домашней странице. Я бы порекомендовал просто использовать его некоторое время, вы освоитесь. - person Tavian Barnes; 07.02.2014
comment
Здесь у вас есть addTo в обратном порядке. @Module(addsTo = MyAppModule.class) class SubModule {}` предназначен не для разрешения graph.plus(new MyAppModule);, а для разрешения ObjectGraph.create(new MyAppModule()) и затем graph.create(new SubModule()); Дочерний модуль добавляет к родительскому в том смысле, что созданный граф является расширение, объединение дочерних и родительских графов. Однако решение правильное - вам вообще не нужны addTo в этой ситуации. - person Christian Gruber; 21.02.2014
comment
@ChristianGruber Ваш комментарий на некоторое время смутил меня, потому что я думаю, что вы имели в виду graph.plus(new SubModule()), а не graph.create. Но независимо от того, вы правы, я исправлю ответ. - person Tavian Barnes; 24.02.2014
comment
Упс. Да - это то, что я имел в виду. :) граф.плюс(...) - person Christian Gruber; 24.02.2014