2015-04-09 2 views
24

Я, вероятно, что-то пропустил, но я думал, что Scopes, подобные @Singleton, используются для определения «скопированных жизненных циклов».Области в кинжале 2

Я использую Dagger 2 в приложении для Android (но я не думаю, что проблема связана с Android).

меня есть 1 модуль:

@Module public class MailModule { 

    @Singleton @Provides public AccountManager providesAccountManager() { 
    return new AccountManager(); 
    } 

    @Singleton @Provides public MailProvider providesMailProvider(AccountManager accountManager) { 
    return new MailProvider(accountManager); 
    } 
} 

У меня есть два различных компонента с @Singleton Область применения:

@Singleton 
@Component(modules = MailModule.class) 
public interface LoginComponent { 

    public LoginPresenter presenter(); 
} 


@Singleton 
@Component(
    modules = MailModule.class 
) 
public interface MenuComponent { 

    MenuPresenter presenter(); 

} 

И, MenuPresenter и LoginPresenter, имеют @Inject конструктор. В то время как MenuPresenter ожидает MailProvider в качестве параметра, LoginPresenter принимает AccountManager:

@Inject public MenuPresenter(MailProvider mailProvider) { ... } 

    @Inject public LoginPresenter(AccountManager accountManager) { ... } 

Но каждый раз, когда я использую компоненты для создания MenuPresenter или LoginPresenter я получаю свежий экземпляр MailProvider и AccountManager. Я думал, что они находятся в одном и том же объеме и поэтому должны быть похожими на один (в том же объеме).

Я понял что-то совершенно неправильное. Как определить реальный синглтон для нескольких компонентов в кинжале 2?

ответ

45

Я предполагаю, что LoginComponent и MenuComponent используются отдельно, например. в LoginActivity и MenuActivity. Каждый компонент построен в Activity.onCreate. Если это так, компоненты воссоздаются каждый раз, когда создается новая активность, модули и зависимости, независимо от того, с какой областью они связаны. Поэтому вы получаете новые экземпляры MainProvider и AccountManager каждый раз.

MenuActivity и LoginActivity имеют отдельные livecycles, поэтому зависимости от MailModule не могут быть одноточечными в обоих из них. Вам нужно объявить корневой компонент с областью @Singleton (например, в подклассе Application), от этого зависят MenuComponent и LoginComponent. компонент уровня активности не может быть @Singleton области видимости, лучше создавать свою собственную области действия с помощью @Scope аннотацию, например .:

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

Или вы можете оставить их незаданными.

Что касается областей вообще здесь кратко от начальной Dagger 2 proposal:

@Singleton 
@Component(modules = {…}) 
public interface ApplicationComponent {} 

Это заявление позволяет кинжалом применять следующие ограничения:

  • Данный компонент может только иметь привязок (включая масштаб аннотаций на классах), которые не облагаются полномочиями или объявлены. I.e. компонент не может представлять две области. Если в списке нет области видимости , привязки могут быть недоступны.
  • Укомплектованный компонент может иметь только одну зависимую область. Это механизм, который обеспечивает, чтобы два компонента не объявляли свои собственные привязки с привязкой к . Например. Два компонента Singleton, у каждого из которых есть , их собственный кеш @Singleton будет сломан.
  • Объем компонента не должен появляться ни в одной из его транзитивных зависимостей. Например: SessionScoped -> RequestScoped -> SessionScoped не имеет никакого смысла и является ошибкой.
  • @Singleton обрабатывается специально в том смысле, что он не может иметь никаких зависимых областей. Все ожидают, что Синглтон станет «корнем».

Целью этой комбинации правил является обеспечение, что, когда сфера применяется , компоненты состоят с той же структурой, которую мы использовали иметь с Dagger 1,0 плюс() 'd ObjectGraphs, но с возможностью до имеют статические знания всех привязок и их областей. Чтобы поместить другим способом, когда применяются области применения, это ограничивает графики, чем может быть построено только для тех, которые могут быть правильно построены.

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

Надеюсь, что вам поможет :). Это довольно сложно понять быстро, требует времени, для меня, по крайней мере, это было.

+0

Но компонент может иметь только одну аннотацию области, не так ли? Как мне это сделать, если у меня есть компонент приложения с '@ Application' и LoginComponent с областью' @ Activity'? – sockeqwe

+1

Справа. Компонент не может быть аннотирован двумя областями. Компонент области действия будет иметь все зависимости от компонента области приложения, если он определен в аннотации '@Component (dependencies = ApplicationComponent.class)'. Подумайте о компонентах с областями в виде графиков и подграфов. Компонент приложения и его область действия - корневой граф, компонент активности и его область действия - подграф корня. –

+0

Предотвращает использование области '' @ @ Singleton'', чтобы ввести '' '@ Application''' в' '' @ Activity''' область? Например, если MyPresenter имеет область '' @ Application''', и я хочу ввести его в MyActivity, который имеет область '' @ Activity'''. – AAverin

7

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

@Module public class MailModule { 
    @Provides @ApplicationScoped 
    public AccountManager providesAccountManager() { 
    return new AccountManager(); 
    } 

    @Provides @ApplicationScoped 
    public MailProvider providesMailProvider(AccountManager accountManager) { 
     return new MailProvider(accountManager); 
    } 
} 

Тогда MailComponent может быть определена для MailModule. LoginComponent и MenuComponent могут зависеть от MailComponent.

@ApplicationScoped 
@Component(modules = MailModule.class) 
public interface MailComponent { 
    MailProvider mailProvider(); 
    AccountManager accountManager(); 
} 

@ActivityScoped 
@Component(dependencies = MailComponent.class) 
public interface LoginComponent { 
    LoginPresenter presenter(); 
} 

@ActivityScoped 
@Component(dependencies = MailComponent.class) 
public interface MenuComponent { 
    MenuPresenter presenter(); 
} 

The MailComponent может быть инициализирован, как показано ниже, и может быть использовано в MenuComponent и LoginComponent снова показано ниже.

MailComponent mailComponent = DaggerMailComponent.builder().build(); 

DaggerMenuComponent.builder().mailComponent(mailComponent).build(); 

DaggerLoginComponent.builder().mailComponent(mailComponent).build()    
+1

Есть ли разница между написанием '@ApplicationScoped' сверху каждого метода модуля separetly и написанием его поверх @Module один раз? –

+2

@JemshitIskenderov - Вставка аннотаций '@ ApplicationScoped'' 'Module' не будет иметь никакого эффекта. Целью модуля является предоставление зависимостей через методы поставщика (методы, аннотированные аннотацией @ @ Provides). Эти методы поставщиков могут иметь или не иметь областей. Таким образом, становится важным определять области на уровне поставщика. –

Смежные вопросы