简体   繁体   中英

How to use @Scope in Dagger 2

I read a lot of material on the @Scope annotation but still not fully understanding if what I know is correct.

Can someone explain what exactly is @Scope for? Where is it used? How is it used?

Is it only used when defining the component?

Also is a scope defined like this?:

@Scope
public @interface SomeScope {
}

And used like this?:

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

The definition of scopes for Java EE applications states the following:

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.

Let's associate it with Android. Scopes impose certain limitations in terms of dependencies lifecycle provided by a scoped provider.

For example, let's say we want to be provided with @Singleton dependency obtainable from @ActivityScope d component. The component lives as long as the Activity. Once activity is destroyed and created again, our component is instantiated accordingly and our '@Singleton' dependency is also created once again.

Summing up, our @Singleton dependencies live as long as the @Components they are associated with - this is I think most practical explanation.

Internally, once Dagger notifies within the @Component the provision method with the specified scope, it creates a ScopedProvider for dependency provision. Otherwise, a Factory is created.

The ScopedProvider 's get method is a double-check singleton method:

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;
  }

To see a practical @Scope usage you can take a look at one of the examples of mine:

https://github.com/dawidgdanski/AccountAuthenticatorExample

Hope, that was helpful somehow.

EDIT 1 : The article provided by @Fshamri explains it even better.

EDIT 2 :

Let's consider the following structure :

The Component:

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

And the module supplying the dependencies:

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

The @ActivityScope extends the @Scope annotation - it is interpreted by Dagger the same way as the @Singleton is. The ActivityModule provides 2 views: the @ActivityScope d MainView and the SignInView without any scope. During the compilation time Dagger's annotation processor creates Provider s for both views. The difference between the MainView's and the SignInView's generated Provider is that for the MainView Dagger generates the ScopedProvider (because we explicitly request this kind of provider with @ActivityScope ) whereas for the SignInView Dagger generates regular Factory provider.

The contract of the ScopedProvider looks like the one above. The contract for the Factory provider, however, looks the following way:

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

Conclusions: 1. The ScopedProvider is a classical double-check singleton pattern. It guarantees to create only one instance of the dependency. 2. The Factory 's get() method just distributes the dependency from the module which means it can distribute new instance of the dependency each time it is requested (in fact, in the example the Activity is only cast to the SignInView which still gives us a single instance but this is copy-pasted logic from my example). 3. Dagger cares about @Scopes provided along with provision methods in the @Modules, not the @Components.

I encourage you to download the sample and build the project, then to see the contract of the generated Dagger components and modules.

Hope this is more understandable now.

have you looked at 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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM