Reputation: 1066
I'm playing around trying to learn Dagger2. Just when I thought I was getting it, I seem to have gotten stuck. My application has two components, ApplicationComponent (singleton) and StripeComponent (1:1 activity) , which inherit an empty interface for the sake of readability.
Then it has two modules, ApplicationModule and StripeModule.
@Singleton @Component(modules = ApplicationModule.class)
public interface ApplicationComponent extends AbstractComponent ...
@PerActivity @Component(modules = { StripeModule.class }) public interface StripeComponent
extends AbstractComponent ...
@Module public class ApplicationModule
@Module public class StripeModule
One of the objects ApplicationModule provides is a Navigator, and I'm fairly sure than the way it does it is fairly correct:
@Provides @Singleton Navigator provideNavigator() {
return new Navigator();
}
This is a very simple class with pretty much nothing in it yet:
@Singleton public class Navigator
Then when I generate the code, an extra provision factory is generated from StripeModule - StripeModule_ProvideNavigatorFactory. And then the compiler whines that I'm not providing it - which is true, and intentional. It should be provided by the application component only. The question is, why is this factory being generated then? Why doesn't Dagger2 understand that StripeModule is not supposed to provide a navigator?
Upvotes: 0
Views: 200
Reputation: 411
You haven't included the exact error, or the code that actually depends on the Navigator
.
But I'll assume the class that depends on Navigator
is provided from within StripeModule
or another module installed into StripeComponent
.
StripeComponent
and ApplicationComponent
have to be related in some way in order for bindings in StripeComponent
to use bindings provided by ApplicationComponent
.
You can relate them either using component dependencies or subcomponents.
If you use subcomponents, you'd make StripeComponent
a subcomponent of ApplicationComponent
, which would mean that it can use any of the bindings in modules installed in ApplicationComponent
, including the one for Navigator
.
If you want to use component dependencies, you'd make StripeComponent
depend on ApplicationComponent
, and you'd add a method Navigator navigator()
to ApplicationComponent
. Bindings in StripeComponent
can depend on any type returned by a method on a component it depends on.
Upvotes: 0
Reputation: 81539
Assuming you have a StripeActivity
class that uses StripeComponent
to inject itself, then you might end up with a scenario like this one
public class StripeActivity extends AppCompatActivity {
@Inject
Navigator navigator;
@Override
public void onCreate(Bundle saveInstanceState) {
super.onCreate(saveInstanceState);
CustomApplication application = (CustomApplication)getApplicationContext();
StripeComponent stripeComponent = createComponent(application);
stripeComponent.inject(this);
}
protected StripeComponent createComponent(CustomApplication application) {
return DaggerStripeComponent.builder()
.applicationComponent(application.getApplicationComponent())
.build();
}
}
public class CustomApplication extends Application {
ApplicationComponent applicationComponent;
@Override
protected void onCreate() {
super.onCreate();
applicationComponent = createApplicationComponent();
}
protected ApplicationComponent createApplicationComponent() {
return DaggerApplicationComponent.create();
}
public ApplicationComponent getApplicationComponent() {
return applicationComponent;
}
}
@Component(modules={ApplicationModule.class})
@Singleton
public interface ApplicationComponent {
Navigator navigator();
}
@Component(dependencies={ApplicationComponent.class}, modules={StripeModule.class})
@PerActivity
public interface StripeComponent extends ApplicationComponent {
void inject(StripeActivity stripeActivity);
}
@Module
public class ApplicationModule {
@Provides
@Singleton
Navigator navigator() {
return new Navigator();
}
}
@Module
public class StripeModule {
//@Provides
//@PerActivity
//...
}
@Scope
@Retention(RUNTIME)
public @interface PerActivity {
}
EDIT: For base class injection, you need to inject both the superclass and the subclass manually, and you need to specify both the superclass and the subclass in your component. In this case, it would work like this.
public abstract class BaseActivity extends AppCompatActivity {
@Inject
Navigator navigator;
@Override
public void onCreate(Bundle saveInstanceState) {
super.onCreate(saveInstanceState);
CustomApplication application = (CustomApplication)getApplicationContext();
ApplicationComponent component = createComponentAndInjectSelf(application);
component.inject(this);
}
protected abstract ApplicationComponent createComponentAndInjectSelf(CustomApplication application);
}
public class StripeActivity extends BaseActivity {
@Override
public void onCreate(Bundle saveInstanceState) {
super.onCreate(saveInstanceState);
}
@Override
public StripeComponent createComponentAndInjectSelf(CustomApplication application) {
StripeComponent stripeComponent = DaggerStripeComponent.builder()
.applicationComponent(application.getApplicationComponent())
.build();
stripeComponent.inject(this);
return stripeComponent;
}
}
@Component(modules={ApplicationModule.class})
@Singleton
public interface ApplicationComponent {
Navigator navigator();
void inject(BaseActivity baseActivity);
}
@Component(dependencies={ApplicationComponent.class}, modules={StripeModule.class})
@PerActivity
public interface StripeComponent extends ApplicationComponent {
void inject(StripeActivity stripeActivity);
}
Upvotes: 3