I'm trying to achieve encapsulation by using subcomponent which is described here, but I got infinite recursion.
Here is my code:
//tried adding @ScopeA, still the same.
public class A {
@Inject
A(B b) {
}
}
@ScopeA
public class B {
@Inject
B() {
}
}
@Component(modules = AModule.class)
@Singleton
public interface AComponent {
public A a();
}
@Module(subcomponents = SComponent.class)
class AModule {
@Provides
@Singleton
A a(SComponent.Factory factory) {
return factory.component().a();
}
}
@Subcomponent
@ScopeA
interface SComponent {
@ScopeA
A a();
@Subcomponent.Factory
interface Factory {
SComponent component();
}
}
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
DaggerAComponent.create().a();
}
}
After checking generated dagger code, I found this:
private final class SComponentImpl implements SComponent {
private SComponentImpl() {}
@Override
public A a() {
return DaggerAComponent.this.aProvider.get();
}
}
It seeems that SComponent are getting A
from parent component, which is not what I wanted, where is the problem of my code?
Note that the example from the Subcomponents for Encapsulation page uses a qualifier annotation, @PrivateToDatabase
, which is not a scoping annotation and which distinguishes the binding of Database
from the binding of @PrivateToDatabase Database
.
Subcomponents inherit all of the bindings from their parent components, so you currently do have A available from the parent component and also A available from the subcomponent. This is especially tricky if anything in your subcomponent needs to inject A, if it weren't marked @Singleton
: Do you want the A from the parent component, or the A from the subcomponent?
Another tricky part of this situation is that you can't use qualifier annotations on classes that use @Inject
constructors.
I'd recommend that you do the following:
@Provides
method that gets an A instance from the subcomponent.If you'd rather not extract an interface, you could also work around this problem by removing @Inject
from A and writing a @Provides
method in a module in the subcomponent that returns a qualified A, so the unqualified A goes through the top-level component and the qualified A is only available within the subcomponent.