Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Dagger 2: Unable to inject singleton in other scope

I have Singleton scoped module that provides some standard singletons: Application, DB services, etc. But for Activity I have separate module that should create Presenter for he Activity and I need to pass Application context to it. However I get following error when trying to compile the project:

Error:(13, 1) error: xxx.SplashComponent scoped with @xxx.ViewScope may not reference bindings with different scopes:
@Provides @Singleton xxx.ApplicationModule.provideAppContext()

Here is snippet of my Application module:

@Singleton
@Module
public class ApplicationModule {

    private Application app;

    public ApplicationModule(Application app) {
        this.app = app;
    }

    @Provides
    @Singleton
    @Named("ui")
    Scheduler provideUIScheduler() {
        return AndroidSchedulers.mainThread();
    }

    @Provides
    @Singleton
    @Named("io")
    Scheduler provideIOScheduler() {
        return Schedulers.io();
    }

    @Provides
    @Singleton
    Application provideApplication() {
        return app;
    }

    @Provides
    @Singleton
    Context provideAppContext() {
        return app;
    }
}

And here is Activity module and Component:

@Module
public class SplashModule {
    private final FragmentManager fragmentManager;

    public SplashModule(FragmentManager fragmentManager) {

        this.fragmentManager = fragmentManager;
    }

    @Provides
    @ViewScope
    Presenter getPresenter(Context context) {
        return new SplashPresenter(context, fragmentManager);
    }
}

Component:

@ViewScope
@Component(modules = {SplashModule.class, ApplicationModule.class})
public interface SplashComponent {
    void inject(SplashActivity activity);
}

What am I doing wrong?

like image 404
Heisenberg Avatar asked Mar 13 '16 15:03

Heisenberg


2 Answers

I want to explain some key points of Dagger 2 from my understanding.

Main actors:

  • "Component" is the bridge between modules and places where injection happens.

  • "Module" is the place where we declare our objects which will be injected.

  • "Scope" is like the life-time of related injection story.

How does it work?

  • Declare component with a scope ("Singleton").
  • Define modules that can inject the required objects in the component's modules list.

void inject(BaseFragment baseFragment);

*******Expose the provided objects in the component's module to sub components****
DbHelper dbHelper();

  • Declare module and provides objects to be injected via component.

Ex:

@Singleton 
@Component(modules = { ApplicationModule.class, NetworkModule.class })
public interface ApplicationComponent {

  void inject(BaseActivity baseActivity);
  DbHelper dbHelper();
}




@PerService @Component(dependencies = ApplicationComponent.class, modules = ServiceModule.class)
public interface ServiceComponent {

  void inject(SyncService service);

}

// SyncService.java

  @Inject DbHelper dbHelper;  (even Singleton scoped)

  private void setupInjector() {
    ServiceComponent mServiceComponent = DaggerServiceComponent.builder()
        .applicationComponent(getApplicationComponent())
        .serviceModule(new ServiceModule(this))
        .build();
    mServiceComponent.inject(this);
  }

ok then...enter image description here

You can inject both unscoped and (Singleton and PerService) scoped objects to your SyncService.class

like image 53
huseyin Avatar answered Sep 23 '22 21:09

huseyin


What am I doing wrong?

This:

@ViewScope
@Component(modules = {SplashModule.class /*View scoped*/,
    ApplicationModule.class/*Singleton scoped*/})

You can only include unscoped or modules scoped with the same scope in your components. You will need to use more than one component.

To include the dependencies from your application, you need to have them in a different component, e.g. ApplicationComponent. If yo do this, you have 2 options: either declare SplashComponent as a SubComponent of ApplicationComponent or add ApplicationComponent as a dependency to your component. If you add it as a dependency, be sure to also provide methods in your ApplicationComponent, so that it can access the dependencies.

e.g. if you were to use component dependencies:

@Component(modules = {ApplicationModule.class})
public interface ApplicationComponent {

    void inject(MyApplication app);

    // todo: also add getters for your other dependencies you need further down the graph
    Application getApplication();

}

@Component(modules = {SplashModule.class}, dependencies={ApplicationComponent.class})
public interface SplashComponent {
    // as before
}
like image 40
David Medenjak Avatar answered Sep 22 '22 21:09

David Medenjak