26

我可能错过了一些东西,但我认为像@Singleton 这样的作用域用于定义“作用域生命周期”。

我在 Android 应用程序中使用 Dagger 2(但我认为问题根本与 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) { ... }

但是每次我使用组件创建一个MenuPresenterLoginPresenter我都会得到一个新的MailProviderand实例AccountManager。我认为它们在同一范围内,因此应该是一种单例(在同一范围内)。

我是不是完全理解错了。如何在匕首 2 中为多个组件定义一个真正的单例?

4

2 回答 2

53

我假设LoginComponentandMenuComponent是单独使用的,例如在LoginActivityand中MenuActivity。每个组件都内置在Activity.onCreate. 如果是这样,则每次创建新活动、模块和依赖项时都会重新创建组件,而与它们绑定的范围无关。MainProvider因此,您AccountManager每次都会获得新的实例。

MenuActivity并且LoginActivity有单独的生命周期,因此它们的依赖关系MailModule不能是单例的。您需要的是声明具有@Singleton范围的根组件(例如在 Application 子类中),制作MenuComponentLoginComponent依赖它。活动级别组件不能是@Singleton 范围,最好使用@Scope注释创建自己的范围,例如:

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

或者,您可以将它们设为无范围。

关于范围,这里是最初Dagger 2 提案的简要说明:

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

该声明使 Dagger 能够强制执行以下约束:

  • 一个给定的组件可能只有非作用域或声明作用域的绑定(包括类上的作用域注释)。即一个组件不能代表两个范围。当没有列出范围时,绑定只能是无范围的。
  • 一个作用域组件可能只有一个作用域依赖。这是强制两个组件不各自声明自己的作用域绑定的机制。例如,每个都有自己的@Singleton Cache 的两个 Singleton 组件将被破坏。
  • 组件的范围不得出现在其任何传递依赖项中。例如: SessionScoped -> RequestScoped -> SessionScoped 没有任何意义并且是一个错误。
  • @Singleton 被特殊对待,因为它不能有任何范围依赖。每个人都希望 Singleton 成为“根”。

这种规则组合的目标是强制在应用范围时,组件的组成与我们过去在 Dagger 1.0 plus()'d ObjectGraphs 中的结构相同,但能够静态了解所有绑定及其范围。换句话说,当应用范围时,这会将图形限制为只能构建那些可以正确构建的图形。

从我自己的实践来看,根本不使用更清楚@Singleton。取而代之的是,我使用@ApplicationScope. 它用于在整个应用程序上定义单例,并且没有额外的限制@Singleton

希望对你有帮助:)。快速理解是相当棘手的,需要时间,至少对我来说是这样。

于 2015-04-14T05:13:39.830 回答
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于。MenuComponentMailComponent

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

可以如下所示进行初始化,并且MailComponent可以在MenuComponent下面LoginComponent再次显示。

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

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

DaggerLoginComponent.builder().mailComponent(mailComponent).build()            
于 2015-04-26T09:31:21.393 回答