39

I've looked at a couple different articles which seem to suggest two different ways of doing custom scoping in Dagger 2:

  1. MVP Presenters that Survive Configuration Changes Part-2 (Github repo):

    • Uses unique custom scopes for each fragment, e.g. @Hello1Scope and @Hello2Scope for Hello1Fragment and Hello2Fragment respectively
  2. Tasting Dagger 2 on Android:

    • Uses a single custom scope for all fragments, e.g. @PerFragment.

From what I understand, it seems that, as in method 2, it should be okay to have a single scope defined that can be used for all fragments (i.e., @PerFragment). In fact (please correct me if I'm wrong), it seems like the name of the custom scope is irrelevant, and it's only where the subcomponent is created (i.e. in Application, Activity, or Fragment) that matters.

Is there any use case for defining a unique scope for each fragment such as in case 1?

bcorso
  • 45,608
  • 10
  • 63
  • 75

2 Answers2

95

After reading the answer by @vaughandroid, and What determines the lifecycle of a component (object graph) in Dagger 2? I think I understand custom scopes well enough to answer my own question.

First, here are a couple rules when dealing with components, modules, and scoping annotations in dagger2.

  • A Component must have a (single) scope annotation (e.g. @Singleton or @CustomScope).
  • A Module does not have a scope annotation.
  • A Module Method may have a (single) scope that matches its Component or no scope, where:
    • Scoped: means a single instance is created for each instance of the component.
    • Unscoped: mean a new instance is created with each inject() or provider call
    • NOTE: Dagger2 reserves @Singleton for the root Component (and it's modules) only. Subcomponents must use a custom scope, but the functionality of that scope is exactly the same as @Singleton.

Now, to answer the question: I would say create a new named scope for each conceptually different scope. For example, create a @PerActivity, @PerFragment, or @PerView annotation that indicates where the component should be instantiated, and thus indicating its lifetime.

Note: this is a compromise between two extremes. Consider the case of a root component and n subcomponents you will need:

  • at least 2 annotations (@Singleton and @SubSingleton), and
  • at most n+1 annotations (@Singleton, @SubSingleton1, ... @SubSingletonN).

Example:

Application:

/** AppComponent.java **/ 
@Singleton
@Component( modules = AppModule.class )
public interface AppComponent{
    void inject(MainActivity mainActivity);
}

/** AppModule.java **/
@Module
public class AppModule{
    private App app;

    public AppModule(App app){
        this.app = app;
    }

    // For singleton objects, annotate with same scope as component, i.e. @Singleton
    @Provides @Singleton public App provideApp() { return app; }
    @Provides @Singleton public EventBus provideBus() { return EventBus.getDefault(); }
}

Fragment:

/** Fragment1Component.java **/
@PerFragment
@Component( modules = {Fragment1Module.class}, dependencies = {AppComponent.class} )
public interface Fragment1Component {
    void inject(Fragment1 fragment1);
}

/** Fragment1Module.java **/ 
@Module
public class Fragment1Module {
    // For singleton objects, annotate with same scope as component, i.e. @PerFragment
    @Provides @PerFragment public Fragment1Presenter providePresenter(){
        return new Fragment1Presenter();
    }
}

/** PerFragment.java **/ 
@Scope
@Retention(RetentionPolicy.RUNTIME)
public @interface PerFragment {}
Community
  • 1
  • 1
bcorso
  • 45,608
  • 10
  • 63
  • 75
  • Could you provide details on these rules? – peacepassion Jul 05 '15 at 12:39
  • @peacepassion not sure exactly what kind of details you are after, but I updated the answer with a sample from a project I've done. – bcorso Jul 05 '15 at 22:25
  • Where would you instantiate `Fragment1Component`, and where would you use it? `onCreateView`? – mbmc Jul 30 '15 at 04:28
  • @tibo I would suggest to instantiate it in `onCreate`. – Giulio Piancastelli Aug 04 '15 at 08:39
  • based on similar readings about scopes and configuration changes and custom scopes I ended up putting together this little [library](https://github.com/laenger/together-mvp) to help with scope/component creation for each fragment/activity, presenter binding etc. maybe it's something that you find useful – laenger Oct 14 '16 at 22:45
  • So if I annotate methods in my subcomponent with `@Singleton` or `@ElectionScope2k16` it would make no difference compared to annotating with `@ActivityScope?` It would in any way (unless unscoped, which causes creation on each inject) just be bound to the subcomponents lifecycle? – Zackline Nov 08 '16 at 19:27
22

Your understanding is correct. The named scopes allow you to communicate intention, but they all work the same way.

  • For scoped provider methods, each Component instance will create 1 instance of the provided object.
  • For unscoped provider methods, each Component instance will create a new instance of the provided object whenever it needs to inject it.

The lifetime of the Component instance is important though. 2 different instances of the same component will provide different object instances, even scoped ones.

Scope names should indicate the lifetime of the provided object (which matches that of the Component instance) so @PerFragment makes much more sense to me.

From a quick look at the "MVP Presenters..." tutorial, it's not clear to me exactly what the author's intention is with having separate scopes. Since the names are just throwaway ones, I wouldn't read too much into it.

vaughandroid
  • 4,315
  • 1
  • 27
  • 33
  • I'm guessing he plans to "encapsulate the logic into custom views" and so each view has its own presenter, and each presenter has its own scope. – EpicPandaForce Jun 22 '15 at 11:17
  • @EpicPandaForce yes, you're right that in method 1 the intention is to associate each "MVP-View" with an "Android-Custom-View" where each View then has it's own Presenter (Although, usually I've seen MVP-View associated with Fragments/Activities). However, the same argument seems to apply -- i.e., use `@PerView` rather than every view having it's own scope, right? – bcorso Jun 22 '15 at 13:56
  • Maybe I'm just a mediocre programmer, but as much as I like DI, MVP and all the clean code stuff, this "MVP Presenters..." series feels awfully overengineered to me. I pity whoever ends up having to maintain a codebase with the likes of `public abstract class PresenterControllerFragment, P extends Presenter> extends ComponentControllerFragment` and `ComponentCacheDelegate` and all of that. – Konrad Morawski Aug 15 '16 at 11:33