Reputation: 6026
I have 2 components: the AppComponent
and the ApiComponent
. I would like to use the dependencies provided by the AppComponent
in the ApiComponent
and in the objects to which the ApiComponent
is injected. So I see the ApiComponent
as a sub component of the AppComponent
. I have declared the AppComponent
as a dependency in the ApiComponent
using the dependecies
directive:
@ApiScope
@Component(dependencies = { AppComponent.class},
modules = { ApiModule.class })
public interface ApiComponent {
void inject(Application application);
void inject(IntentService1 service1);
SampleApi sampleApi();
}
Here is my AppComponent:
@Singleton
@Component (modules = { AppModule.class })
public interface AppComponent {
void (Class2 class2);
Bus bus();
SharedPreferences sharedPreferences();
SampleApplication sampleApplication();
}
The relevant part of my ApiModule looks like this:
@Module
public final class ApiModule {
@Provides
@ApiScope
SampleApi provideSampleApi(Retrofit retrofit) {
return retrofit.create(SampleApi.class);;
}
}
I trigger the injection in onCreate() method of my IntentService1:
@Inject SampleApi sampleApi;
@Override
public void onCreate() {
SampleApplication.get().getApiComponent().inject(this);
}
But I get the following compile error:
SampleApi cannot be provided without an @Provides or @Produce-annotated method
Does anyone have a clue what's going on? I appreciate your help.
Upvotes: 2
Views: 2309
Reputation: 6026
My problem was with scopes. I was using incorrect annotations for declaring a scope. This is how I declare a scope now:
@Retention(RetentionPolicy.RUNTIME)
@Scope
public @interface ApiScope {
}
It's annoying that a dependent component cannot have the same Singleton scope as their parent component, and that you have to declare named scopes for all of your singleton components, but the reason is described here. Also, please make sure that ALL of your provider methods in your module are annotated with the same scope as your component's scope. Here's one of my provider methods:
@Provides
@ApiScope
UserApi provideUserApi(Retrofit retrofit) {
return retrofit.create(UserApi.class);
}
And make sure you explicitly expose the dependencies from the parent component by declaring methods with the same name as the dependency they provide (except for capitalizing the first letter), BOTH in the parent component (interface) and in the dependent component, like this:
Bus bus();
SharedPreferences sharedPreferences();
MyApplication myApplication();
Also make sure to expose the dependencies your (dependent) module provides in your (dependent) component, again the exposer method's name should be identical to your dependency's name, except for the first letter:
UserApi userApi();
Also make sure to check out this very useful and accurate article on Dagger 2. This stackoverflow answer helped me to pinpoint my problem about declaring scopes, and to manage the dependencies' lifecycle.
PS: I avoid using the term "subcomponent" because there is a different way for declaring a "subcomponent" in Dagger 2, even though depedent components and subcomponents are conceptually identical.
Upvotes: 0
Reputation: 6452
I'm on this case too. I believe what you are wanting here is a @Subcomponent
. I believe the dependencies directive is for when your lower level module (avoiding using the word 'sub' for clarity) does not know (or want to know) about those dependencies declared in your 'root' module (i.e. the module with items such as the event bus). Quoting the Dagger 2 documentation about component dependencies = { }
;
Component Dependencies
While subcomponents are the simplest way to compose subgraphs of bindings, subcomponents are tightly coupled with the parents; they may use any binding defined by their ancestor component and subcomponents. As an alternative, components can use bindings only from another component interface by declaring a component dependency. When a type is used as a component dependency, each provision method on the dependency is bound as a provider. Note that only the bindings exposed as provision methods are available through component dependencies.
I've tried re-writing your code to help but I can't say I understand it all so I'll show you how I've used this construct just recently within my app. Hope this helps and you can draw the parallels between your case and this.
So....
Scenario: My SplashActivity
contributes a LocalBroadcastManager
dependency and activity Context
to the root module's graph and uses a database dependency provided by the main module...very similar to your use case.
@PerActivity
@Subcomponent(
modules = SplashActivityModule.class
)
public interface SplashActivityComponent {
void inject(final SplashActivity splashActivity);
}
Snippet 1: Splash activity sub-component
@Module
public class SplashActivityModule {
private final Context activity;
/**
* Constructs the activity module.
*
* @param activity The activity context.
*/
public SplashActivityModule(final Activity activity) {
this.activity = activity;
}
/**
* Provide the (domain) context.
*
* @return The context of the domain module.
*/
@Provides
@PerActivity
Context provideContext() {
return activity;
}
/**
* Provide the local broadcast manager.
*
* @return the broadcast manager.
*/
@Provides
@PerActivity
LocalBroadcastManager provideLocalBroadcastManager() {
return LocalBroadcastManager.getInstance(activity);
}
}
Snippet 2: Injection instructions for the activity aka the SplashActivityModule
@Component(modules = DomainModule.class)
public interface DomainComponent {
SplashActivityComponent plus(final SplashActivityModule splashActivityModule);
}
Snippet 3: Parent (or root) module providing an entry-point into the graph.
@Override
protected void setupActivityComponent(final DomainComponent domainComponent) {
domainComponent.plus(new SplashActivityModule(this)).inject(this);
}
Snippet 4: SplashActivity
code doing the injection (called immediately after onCreate
's super call)
Hope that helps. Keep me posted on your findings with this because I am struggling with the scenario whereby the child module cannot know about the parent ...i.e. not a @Subcomponent
.
Upvotes: 2