8
votes

I'm struggling with dagger 2 in order to understand how i can pass a context or another according to my needs. - First I have an ApplicationModule annotated @Singleton since it provides hi level objects like the webservice object, the model ..., generally those objects are passed the ApplicationContext (since the y need to live during the whole Application lifetime)

@Singleton
@dagger.Component(modules = {
    AppModule.class
})
public interface AppComponent {
        void inject(MyApp application);
        Model model();
        Context context();<--- should provide the application Context for the Object above (model)
...

the implementation looks like that

@dagger.Module
public class AppModule {

    private final Application app;
    public ApplModule(Application app) {
        this.app = app;
    }

    @Provides
    @Singleton
    Model provideModel(Bus bus) {
        return new Model(bus);
    }

    @Provides
    @Singleton
    Context provideApplicationContext() {
        return app.getApplicationContext();
    }
...
  • secondly I have an Activity Scope componenet in with I provide the current activity and different views which need a Context.

      @ActivityScope
        @Component(
                dependencies = AppComponent.class
                , modules = {ActivityModule.class}
        )
        public interface ActivityComponent {
            void inject(MyActivity activity);
            Context context(); <---should provide the activity's context
            MyView homeView(); <----takes a Context as a contructor parameter
    

    @Module public class ActivityModule { private final Activity activity;

    public ActivityModule(Activity activity) {
    
        this.activity = activity;
    }
    
    @Provides
    @ActivityScope
    public Activity activity() {
        return activity;
    }
    
    
    @Provides
    @ActivityScope
    @Named("viewcontext") <----- if I removed this I get an error from Dagger
    public Context context() {
        return activity;
    }
    
    @Provides
    @ActivityScope
    MyView provideZeView(Bus bus, Model model) { <---- previously receiving the ApplicationContext as a parameter 
        MyView v = new MyView(activity, bus, model); <---- must pass the activity otherwise passing the Context reveived is the ApplicationContext
        return v;
    }
    

    so Here are my questions:

  • I used scopes in order to have a better "granularity" over what is passed and i still get the applicationContext
  • If I remove the @Named qulifier i get an error
  • previously the Views where produced by another module with a dependence to the ActivityModule but still getting the ApplicationContext

Well the point is I am certainly missing something...but I can't figure what, maybe I misundestood the use of Scopes

2
Considering you have two Context which are pretty much the same, you need to specify @Named("application") and @Named("activity") on them. - EpicPandaForce
Although actually, you can just not add the Context context() to your Component, only to the module, and it'll also work! - EpicPandaForce
Because Dagger fulfills an injection based on type having two methods which provide a Context class can cause an error which is why the nice folks included the @Named() annotation. - fakataha

2 Answers

5
votes

you can use qualifiers like this. in two separate files define the following:

@Qualifier
@Retention(RetentionPolicy.RUNTIME)
public @interface ActivityContext {
}

@Qualifier
@Retention(RetentionPolicy.RUNTIME)
public @interface ApplicationContext {
}

then in your ActivityModule do this:

@Provides
@ActivityScope
@ActivityContext
public Context context() {
    return activity;
}

and likewise in your appmodule do this:

 @Provides
    @Singleton
@ApplicationContext
    Context provideApplicationContext() {
        return app.getApplicationContext();
    }

now we have a way to ask for whatever type of context we need based on the qualifier @ApplicationContext and @ActivityContext.

so for example in your activity you could do this:

  @Inject @ApplicationContext
    Context c;

which would inject an application context.

and in a module you could do this for example:

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

to provide an activity context. this is just an example.

0
votes

@Named is a requirement if you want to provide multiple objects of the same type from your module.

As far as your second question, in regards to passing the correct Activity context, you need to have this in your ActivityComponent:

Activity activity();