11

I'm using Dagger 2 with simple MVP pattern. I have a @PerApp and @PerActivity scopes. I'm injecting presenters dependencies using constructor injection, which makes those presenters "injectable" (I don't need to write provides methods in activity module). Code fragments:

PerApp:

// AppComponent
@PerApp
@Component(modules = {AppModule.class, DataModule.class, NetworkModule.class})
public interface AppComponent {
    LoginComponent plus(LoginModule loginModule);

    MainComponent plus(MainModule mainModule);
}

// Example @PerApp module
@Module
public class NetworkModule {

    @Provides
    @PerApp
    Retrofit providesRetrofit(){
         ...
    }
}

PerActivity:

// LoginModule
@Module
public class LoginModule {
    private final LoginActivity mLoginActivity;

    public LoginModule(LoginActivity loginActivity) {
        mLoginActivity = loginActivity;
    }

    @Provides
    @PerActivity
    Context providesContext() {
        return mLoginActivity;
    }
}

// LoginComponent
@PerActivity
@Subcomponent(
        modules = LoginModule.class
)
public interface LoginComponent {
    void inject(LoginActivity loginActivity);
}

Activity:

public class LoginActivity {
    @Inject LoginPresenter mPresenter;
}

Presenter:

public class LoginPresenter {

    @Inject
    public LoginPresenter(Retrofit retrofit) {
        ...
    }
}

It's working great. My question is: What will be the scope of provided LoginPresenter? Is it gonna be the same as LoginActivity? Should I annotate the presenter's constructor with @PerActivity or something?

3 Answers 3

6

I've ran some tests to check by myself how exactly scoping works with constructor injection and here are my results.

1) Inject LoginPresenter into LoginActivity:

LoginComponent - @PerActivity scope (code is exactly the same as in my first post).

I've tried to inject presenter into 2 variables:

public class LoginActivity {
    @Inject LoginPresenter A;
    @Inject LoginPresenter B;
}

LoginPresenter annotated with:

  • nothing - A and B are different
  • @PerActivity - A and B are the same
  • @PerApp - A and B are the same

2) Inject LoginPresenter into LoginActivity and MainActivity:

LoginComponent, MainComponent - @PerActivity scope (code is exactly the same as in my first post).

I've tried to inject presenter into 2 different activities:

public class LoginActivity {
    @Inject LoginPresenter A;
}

public class MainActivity {
    @Inject LoginPresenter B;
}

LoginPresenter annotated with:

  • nothing - A and B are different
  • @PerActivity - A and B are different
  • @PerApp - A and B are the same
3

It all depends on the component you're using for injection.

Let's assume:

  1. This is the component you're using for injecting stuff into your LoginActivity:

@PerActivity
@Component(dependencies = ApplicationComponent.class, modules = LoginActivityModule.class)
public interface LoginActivityComponent {
    void inject(LoginActivity loginActivity);
}
  1. You use an ApplicationComponent for providing objects from @PerApp scope

If you don't specify the way a particular module should provide a particular object (LoginPresenter for example), you're basically saying to your component:

I need a LoginPresenter instance. I don't care who creates that. Just figure that out yourself.

This component checks if any of the dependent components/modules knows how to create that. If not, the asked component needs to create that itself (with some help from the dependent dagger elements).

An injecting component can only try to reference ("create") objects that are from the same scope. So if the LoginActivityComponent is from @PerActivity, it can create object that are only from this scope.

If you don't specify the scope in your LoginPresenter, dagger will assume the same scope as the injecting component. If you specify explicitly @PerActivity scope, it will match the component scope and everything will be ok. But if you specify a different scope (e.g.@PerApp), your app won't build correctly since no class from the @PerApp is providing the way of creating LoginPresenter.

Disclamer: There might be few parts of my explanation that I have been simplified, so it's a bit easier to understand (mostly in terms of who exactly does what). I tried not to make any simplifications that contradict the logic of the Dagger2 itself.

0
3

It seems the way to specify the scope when having a constructor with an @Inject annotation is to annotate the class with a scope:

@PerActivity
public class LoginPresenter {

    @Inject
    public LoginPresenter(Retrofit retrofit) {
        ...
    }
}

I assume LoginPresenter gets injected only in LoginActivity, which means Dagger will provide it only once and create a single instance of it. If you injected it multiple times, without the scope annotation you would get different instances.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.