如何在 Dagger 2 中使用@Scope

How to use @Scope in Dagger 2

我阅读了很多关于@Scope 注释的 material 但仍然不完全理解我所知道的是否正确。

谁能解释一下@Scope 到底是做什么用的?它在哪里使用?它是如何使用的?

是否只在定义组件时使用?

作用域也是这样定义的吗?:

@Scope
public @interface SomeScope {
}

然后这样使用?:

@SomeScope
@Component(modules = {
        HeaterModule.class,
        PumpModule.class
})
public interface MainActivityComponent {
    void inject(MainActivity mainActivity);
    CoffeeMaker maker();
}

你看过this article了吗?

In Dagger 2 scopes mechanism cares about keeping single instance of class as long as its scope exists. In practice it means that instances scoped in @ApplicationScope lives as long as Application object. @ActivityScope keeps references as long as Activity exists (for example we can share single instance of any class between all fragments hosted in this Activity). In short - scopes give us “local singletons” which live as long as scope itself.

Java EE 应用程序的范围定义如下:

For a web application to use a bean that injects another bean class, the bean needs to be able to hold state over the duration of the user’s interaction with the application. The way to define this state is to give the bean a scope.

让我们把它与Android联系起来。范围在范围提供者提供的依赖生命周期方面施加了某些限制。

例如,假设我们想要从 @ActivityScoped 组件获得 @Singleton 依赖项。该组件的寿命与 Activity 一样长。一旦 activity 被销毁并再次创建,我们的组件就会相应地实例化,我们的“@Singleton”依赖项也会再次创建。

总而言之,我们的 @Singleton 依赖项只要与它们相关联的 @Components 就存在 - 这是我认为最实用的解释。

在内部,一旦 Dagger 在 @Component 中通知具有指定范围的提供方法,它就会创建一个 ScopedProvider for dependency provision. Otherwise, a Factory

ScopedProvider的get方法是双重检查单例方法:

public T get() {
    // double-check idiom from EJ2: Item 71
    Object result = instance;
    if (result == UNINITIALIZED) {
      synchronized (this) {
        result = instance;
        if (result == UNINITIALIZED) {
          instance = result = factory.get();
        }
      }
    }
    return (T) result;
  }

要查看实用的 @Scope 用法,您可以查看我的示例之一:

https://github.com/dawidgdanski/AccountAuthenticatorExample

希望,这在某种程度上有所帮助。

编辑 1: @Fshamri 提供的文章解释得更好。

编辑 2:

让我们考虑以下结构:

组件:

@ActivityScope //Dagger ignores the annotation put atop the @Component. I put it there just for readability
@Component(dependencies = DependencyGraph.class,
        modules = {ActivityModule.class})
public interface ActivityComponent {
    void inject(MainActivity mainActivity);
    void inject(SignUpActivity signUpActivity);
    void inject(SignInActivity signInActivity);

以及提供依赖项的模块:

    @Module
    public class ActivityModule {

    private final Activity activity;

    public ActivityModule(Activity activity) {
        this.activity = activity;
    }

    @Provides
    @ActivityScope
    MainView provideMainView() {
        return (MainView) activity;
    }

    @Provides
    SignInView provideSignInView() {
        return (SignInView) activity;
    }
    }

两个视图的 @ActivityScope extends the @Scope annotation - it is interpreted by Dagger the same way as the @Singleton is. The ActivityModule provides 2 views: the @ActivityScoped MainView and the SignInView without any scope. During the compilation time Dagger's annotation processor creates Provider。 MainView 和 SignInView 生成的 Provider 之间的区别在于,对于 MainView Dagger 生成 ScopedProvider(因为我们使用 @ActivityScope 明确请求此类提供者),而对于 SignInView Dagger 生成常规Factory 提供商。

ScopedProvider 的合同如上所示。然而,Factory 提供者的合同如下所示:

@Generated("dagger.internal.codegen.ComponentProcessor")
public final class ActivityModule_ProvideSignInViewFactory implements Factory<SignInView> {
  private final ActivityModule module;

  public ActivityModule_ProvideSignInViewFactory(ActivityModule module) {  
    assert module != null;
    this.module = module;
  }
  @Override
  public SignInView get() {  
    SignInView provided = module.provideSignInView();
    if (provided == null) {
      throw new NullPointerException("Cannot return null from a non-@Nullable @Provides method");
    }
    return provided;
  }

  public static Factory<SignInView> create(ActivityModule module) {  
    return new ActivityModule_ProvideSignInViewFactory(module);
  }
}

结论: 1. ScopedProvider 是经典的双重检查单例模式。它保证只创建一个依赖实例。 2. Factoryget() 方法只是分发模块的依赖关系,这意味着它可以在每次请求时分发依赖关系的新实例(事实上,在示例中 Activity 仅转换为 SignInView,它仍然为我们提供了一个实例,但这是从我的示例中复制粘贴的逻辑)。 3. Dagger 关心@Modules 中提供的@Scopes 和provision 方法,而不是@Components。

我鼓励您下载示例并构建项目,然后查看生成的 Dagger 组件和模块的契约。

希望这现在更容易理解了。