Dagger 2 中的作用域

Scopes in Dagger 2

我可能漏掉了什么,但我认为像@Singleton 这样的作用域是用来定义 "scoped lifecycles".

我在 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();

}

MenuPresenterLoginPresenter 都有一个 @Inject 构造函数。 MenuPresenter 需要 MailProvider 作为参数,而 LoginPresenter 需要 AccountManager:

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

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

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

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

我假设 LoginComponentMenuComponent 是分开使用的,例如在 LoginActivityMenuActivity 中。每个组件都内置于 Activity.onCreate。如果是这样,每次创建新 activity 时都会重新创建组件,模块和依赖项也会重新创建,而与它们绑定的范围无关。因此,您每次都会得到 MainProviderAccountManager 的新实例。

MenuActivityLoginActivity 有独立的生命周期,因此来自 MailModule 的依赖项不能在两者中都是单例。您需要的是声明具有 @Singleton 作用域的根组件(例如在 Application 子类中),使 MenuComponentLoginComponent 依赖于它。 Activity 级别组件不能是@Singleton 范围,最好使用 @Scope 注释创建自己的范围,例如:

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

或者你可以让它们没有作用域。

这里是关于范围的简要介绍 Dagger 2 proposal:

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

That declaration enables dagger to enforce the following constraints:

  • A given component may only have bindings (including scope annotations on classes) that are unscoped or of the declared scope. I.e. a component cannot represent two scopes. When no scope is listed, bindings may only be unscoped.
  • A scoped component may only have one scoped dependency. This is the mechanism that enforces that two components don’t each declare their own scoped binding. E.g. Two Singleton components that each have their own @Singleton Cache would be broken.
  • The scope for a component must not appear in any of its transitive dependencies. E.g.: SessionScoped -> RequestScoped -> SessionScoped doesn’t make any sense and is a bug.
  • @Singleton is treated specially in that it cannot have any scoped dependencies. Everyone expects Singleton to be the “root”.

The goal of this combination of rules is to enforce that when scope is applied, components are composed with the same structure that we used to have with Dagger 1.0 plus()’d ObjectGraphs, but with the ability to have static knowledge of all of the bindings and their scopes. To put it another way, when scopes are applied, this limits the graphs than can be built to only those that can be correctly constructed.

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

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

您可以执行以下操作来为多个组件定义一个真正的单例。我假设 @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);
  }
}

那么可以为MailModule定义一个MailComponentLoginComponentMenuComponent 可以依赖于 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();
}

MailComponent 可以如下所示初始化,并且可以在 MenuComponentLoginComponent 中再次使用,如下所示。

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

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

DaggerLoginComponent.builder().mailComponent(mailComponent).build()