Metallica Metallica - 3 months ago 37
Android Question

Dagger 2 sub-component injection error

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.

Answer

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.

Comments