java Dagger 2 中的范围

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/29528599/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-11-02 15:25:54  来源:igfitidea点击:

Scopes in Dagger 2

javaandroiddagger-2

提问by sockeqwe

I probably missed something, but I thought Scopes like @Singleton are used to define "scoped lifecycles".

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

I use Dagger 2 in an Android app (but I don't think the problem is android related at all).

我在 Android 应用程序中使用 Dagger 2(但我认为问题根本与 android 无关)。

I have 1 Module:

我有 1 个模块:

@Module public class MailModule {

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

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

I have two different components with @Singletonscope:

我有两个不同的@Singleton范围组件:

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

  public LoginPresenter presenter();
}


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

  MenuPresenter presenter();

}

Both, MenuPresenterand LoginPresenter, have an @Injectconstructor. While MenuPresenter expects MailProvideras parameter, LoginPresenter takes an AccountManager:

两者,MenuPresenter并且LoginPresenter,有一个@Inject构造函数。虽然 MenuPresenter 需要MailProvider作为参数,但 LoginPresenter 需要一个AccountManager

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

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

But every time I use the components to create a MenuPresenteror LoginPresenterI get a fresh new instance of MailProviderand AccountManager. I thought they were in the same scope and should therefore be kind of singleton (in the same scope).

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

Did I understand something completely wrong. How do I define a real singleton for multiple components in dagger 2?

我是否理解完全错误。如何为 Dagger 2 中的多个组件定义真正的单例?

回答by Kirill Boyarshinov

I assume that LoginComponentand MenuComponentare used separately, e.g. in LoginActivityand MenuActivity. Each component is built in Activity.onCreate. If so, components are recreated every time new activity created, modules and dependencies too, independent of what scope they bond to. Therefore, you get new instances of MainProviderand AccountManagerevery time.

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

MenuActivityand LoginActivityhave separate livecycles, so dependencies from MailModulecannot be singleton in both of them. What you need is to declare root component with @Singletonscope (e.g. in Application subclass), make MenuComponentand LoginComponentdepend on it. Activity level component cannot be @Singleton scoped, better to create your own scopes using @Scopeannotation, e.g.:

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

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

Or you can leave them unscoped.

或者,您可以将它们保留在未范围内。

Regarding scopes at all here's brief from initial Dagger 2 proposal:

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

@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
@Component(modules = {…})
public interface ApplicationComponent {}

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

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

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

From my own practice, it's clearer not to use @Singletonat all. Instead of that, I use @ApplicationScope. It serves to define singletons on whole application and does not have additional restrictions as @Singletonhas.

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

Hope that helps you :). It's quite tricky to be understood quickly, takes time, for me at least it was.

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

回答by Praveer Gupta

You can do the following to define a real singleton for multiple components. I am assuming @ApplicationScopedand @ActivityScopedto be the different scopes.

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

Then a MailComponentcan be defined for the MailModule. The LoginComponentand MenuComponentcan depend on the MailComponent.

然后MailComponent可以为 定义a MailModule。该LoginComponentMenuComponent可以依靠的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 MailComponentcan be initialized as shown below and can be used in MenuComponentand LoginComponentagain shown below.

MailComponent如下图所示,可以用在可被初始化MenuComponentLoginComponent下面再次示出。

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

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

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