Dagger2 Custom Scopes : How do custom-scopes (@ActivityScope) actually work?

匿名 (未验证) 提交于 2019-12-03 01:23:02

问题:

I am reading the source code for Dagger2 Component Scopes Test on GitHub, and I've seen a "custom scope" defined for activities called @ActivityScope, but I've seen it in other projects including the 4-module CleanArchitecture that has its @PerActivity scope.

But literally, the code for the @ActivityScope annotation is the following:

import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy;  import javax.inject.Scope;  /**  * Created by joesteele on 2/15/15.  */ @Scope @Retention(RetentionPolicy.RUNTIME) public @interface ActivityScope { } 

And it is "magically" usable in Modules:

@Module public class ActivityModule {   @Provides @ActivityScope Picasso providePicasso(ComponentTest app, OkHttpClient client) {     return new Picasso.Builder(app)         .downloader(new OkHttpDownloader(client))         .listener(new Picasso.Listener() {           @Override public void onImageLoadFailed(Picasso picasso, Uri uri, Exception e) {             Log.e("Picasso", "Failed to load image: " + uri.toString(), e);           }         })         .build();   } } 

Or the CleanArchitecture example:

@Scope @Retention(RUNTIME) public @interface PerActivity {}  @PerActivity @Component(dependencies = ApplicationComponent.class, modules = ActivityModule.class) public interface ActivityComponent {   //Exposed to sub-graphs.   Activity activity(); }  @Module public class ActivityModule {   private final Activity activity;    public ActivityModule(Activity activity) {     this.activity = activity;   }    /**   * Expose the activity to dependents in the graph.   */   @Provides @PerActivity Activity activity() {     return this.activity;   } } 

I can clearly see that this has to do with JSR-330 custom scopes, but I really don't understand what exactly is happening here to make it so that this code enables the given module and/or what is provided by a given module to depend on the actual Activity lifecycle, and for there to exist only a single instance but only if that given activity is active.

The docs say this:

Scope  Dagger 1 only supported a single scope: @Singleton.  Dagger 2 allows users to any well-formed scope annotation.  The Component docs describe the details of      how to properly apply scope to a component. 

It says to look at the Component docs page, but that gives me 404. I also saw this, but...

May I ask for some help in clarifying why specifying this custom scope magically makes Activity-level scopes work without an issue?

(The answer is, a subscope can receive dependencies from its superscope, and a subscope exists as long as the component does. And that you need to specify the scopes on your modules, and you need to specify your component dependencies to subscope one superscope.)

回答1:

Actually there is no magic. Custom scope annotations are just annotations. They can have any name. Scope annotations serve as a tool for static analysis of dependencies that modules provide and components inject. That's why using @ActivityScope dependency in non-@ActivityScope component will fire a compilation error.

How to define the actual scope is your prerogative. Define the livecycle of your scope component, what time it's created and what time it destroyed - this is your scope. E.g. @ActivityScope is tied to Activity livecycle and defined like that:

private ActivityComponent component;  @Override protected void onCreate(Bundle savedInstanceState) {     component = DaggerActivityComponent.builder().build();     component.inject(this); }  @Override protected void onDestroy() {     component = null;     super.onDestroy(); } 

So there is no magic. Define your scopes by the semantics of using them. You may also find useful this answer and these examples.



回答2:

It is worth noting that apparently Dagger2 creates a single instance per scoped provider in a module per component.

So in order to get a scoped provider in a module, you need to specify the scope for your module's provider method.

@Module public class YourModule {     @Provides     @YourScope //one per component     public Something something() { return new SomethingImpl(); }      @Provides //new instance per injection     public Otherthing otherthing() { return new OtherthingImpl(); } }  @Component @YourScope public interface YourComponent {     Something something();     Otherthing otherthing();      void inject(YourThing yourThing); } 

Afterwards, refer to Kirill's answer; essentially a "scope" by itself only determines that it is a different scope from the other one. Using component dependencies (or subcomponents) creates a subscope.

@Module public class SubModule {     @Provides     @SubScope     public ThatThing thatThing() { return new ThatThingImpl(); } }  @Component(dependencies={YourComponent.class}, modules={SubModule.class}) @SubScope public interface SubComponent extends YourComponent {     ThatThing thatThing();      void inject(SubThing subThing); } 

A component can depend on only one other scoped component.



易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!