Android Dagger 2 - how to make custom scope be a local singleton

Viewed 1310

According to tutorials like this one: in Dagger2 we are able to provide local singleton objects using a custom scope.

I already have a global appComponent and my goal is to create a activity scope that allows a subcomponent to have local singleton providers.

My issue is when i create a custom scope and inject my activity twice, i see that the object being provided is not the same one, even though i tagged it with the custom scope. for the appComponent which uses the @Singleton annotation it works fine though. Lets take a look at how i did this:

Here is the custom activity scope:

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

And here is how i define the subcomponent:

@ActivityScope
@Subcomponent(modules = {PresenterModule.class, UseCaseModule.class})
public interface ActivitySubComponent {
    void inject(LoginActivity target);
    void inject(LoginPresenter target);
    void inject(UCGetFireBaseAccount target);
}

Notice here that i've tagged this subcomponent with @ActivityScope annotation that i custom made above. Now lets see the appComponent which is the parent component and global:

@Singleton
@Component(modules = {AppModule.class, NetworkModule.class, RepositoryModule.class})
public interface AppComponent {
    void inject(myappCloudRepo target);
    void inject (FireBaseCloudRepo target);
    ActivitySubComponent plus(PresenterModule presenterModule, UseCaseModule useCaseModule);
}

Notice ActivitySubComponent is defined here as a subcomponent.

Finally lets look at the presenterModule class to see what i wanted to be provided as a singleton:

@Module
public class PresenterModule {

        @Provides
        @ActivityScope
        LoginPresenter provideLoginPresenter(Context context) {
        return new LoginPresenter(context);
    }
 }

Now when i actually use this i set it up in my application class like this:

public class MyApplication extends Application {

    private AppComponent appComponent;

    @Override
    public void onCreate() {
        super.onCreate();
        appComponent = initDagger(this);
    }

    public AppComponent getAppComponent() {
       return appComponent;
    }

    protected AppComponent initDagger(MyApplication application) {
        return DaggerAppComponent.builder()
                .appModule(new AppModule(application))
                .build();
    }

    public ActivitySubComponent getActivityComponent() {
       return appComponent.plus(new PresenterModule(),new UseCaseModule());
    }
}

Then lastly in any activity i do this:

public class LoginActivity extends AppCompatActivity{

    ActivitySubComponent activityComponent;

    //this loginpresenter is from the activitysubcomponent
    @Inject
    LoginPresenter loginPresenter;

    //this retrofit object is from the appcomponent
    @Inject
    Retrofit retrofit;

   @Override
   protected void onCreate(@Nullable Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);
       activityComponent= ((MyApplication)getApplication()).getActivityComponent();
       activityComponent.inject(this);

       Log.d("j2emanue",loginPresenter.toString());
       Log.d("j2emanue",retrofit.toString());


      activityComponent= ((MyApplication)getApplication()).getActivityComponent();
      activityComponent.inject(this);

      Log.d("j2emanue2",loginPresenter.toString());
      Log.d("j2emanue2",retrofit.toString());
    }
}

When i take a look at the logs after injecting TWICE i am expecting all objects to be the same but instead the loginPresenter is another instance but the retrofit is the same so that works. Here is the log:

05-23 23:33:35.617 31298-31298/com.myapp.mobile.myappfashion D/j2emanue: com.myapp.mobile.myappfashion.UI.Presenters.LoginPresenter@1c27a460
05-23 23:33:35.617 31298-31298/com.myapp.mobile.myappfashion D/j2emanue: retrofit2.Retrofit@13366d19
05-23 23:33:35.617 31298-31298/com.myapp.mobile.myappfashion D/j2emanue2: com.myapp.mobile.myappfashion.UI.Presenters.LoginPresenter@3dc041de
05-23 23:33:35.617 31298-31298/com.myapp.mobile.myappfashion D/j2emanue2: retrofit2.Retrofit@13366d19

Notice the loginpresenter is another object so its not making a local singleton for me that would only be destroyed when i dereference the activitysubcomponent. what am i missing here ? The reason i want it to be a local singleton is for configuration changes. This way the presenter can be maintained during a config change.

1 Answers
Related