To make this work, you need to establish a component dependency from your MainActivityComponent onto your AppComponent.
@Component(
modules = {MainActivityModule.class},
dependencies = {AppComponent.class}) // <-- this
public interface MainActivityComponent {
void inject(MainActivity mainActivity);
}
This tells Dagger that it may call zero-arg methods on AppComponent to supply dependencies that MainActivityComponent needs, including dependencies of objects that MainActivity injects. This makes it particularly well-suited for depending on other Dagger components, since you can add those zero-arg methods onto AppComponent and Dagger will supply the implementations. However, for the sake of clarity or avoiding dependency cycles, you might also choose to list an interface as the dependency and then have a DaggerAppComponent implementation provided instead.
You'll need to supply an implementation of AppComponent as a part of ActivityComponent's builder:
MainActivityComponent mainActivityComponent =
DaggerMainActivityComponent.builder()
.mainActivityModule(new MainActivityModule())
.contextModule(new ContextModule(this))
.appComponent(App.getAppComponent()) // <-- this
.build();
mainActivityComponent.inject(this);
Important: The only bindings available to you in MainActivityComponent will be the ones that are exposed as zero-arg methods on the interface or class that you list as a dependency ("provision methods"). If you supply a binding using a @Provides
method on an AppComponent module, it will not appear in MainActivityComponent unless you add a getter for it on AppComponent itself. This allows you to be specific about what you inherit from AppComponent, but can be seen as a maintenance burden.
@Component(modules = ModuleA.class) public interface ComponentA {
BindingOne bindingOne();
}
@Module public abstract class ModuleA {
@Provides static BindingOne getBindingOne() { return BindingOne.INSTANCE; }
@Provides static BindingTwo getBindingTwo() { return BindingTwo.INSTANCE; }
}
// ComponentB has access to BindingOne but not BindingTwo, because it can only see
// what is available in ComponentA, not ComponentA's module set that includes ModuleA.
@Component(dependencies = ComponentA.class) public interface ComponentB { /* ... *}
Side notes
- If MainActivityModule has a default constructor or other public no-arg constructor, you don't need to supply it in your builder. Dagger will create one for you.
getDecimalFormat
and getPresenter
don't use any instance state, so you can make them static
; Dagger will detect this and make static calls instead of virtual method calls that are slightly slower on Android. If you switch all of your @Module methods to static @Provides
or abstract @Binds
methods, you can change your Module to an interface or abstract class, which means that Dagger can and will skip instantiating the method at all.
- You need to list all of the bindings from AppComponent that you want on MainActivityComponent because Dagger can generate code for AppComponent and MainActivityComponent separately. As an alternative to component dependencies, you can choose to use subcomponents, where Dagger generates code for MainActivityComponent as an implementation detail of AppComponent, and you get your builder from AppComponent instead of a static method on DaggerMainActivityComponent. This establishes a tighter coupling between MainActivityComponent and AppComponent, but allows you to skip defining the zero-arg methods on AppComponent because Dagger can inspect what MainActivityComponent needs at compile time and provide those bindings privately.
- One existing framework for a subcomponent hierarchy of Android Application/Activity/Fragment injection is dagger.android, which automates the creation and installation of subcomponents for each Android-created object including BroadcastReceiver, ContentProvider, and Service. As you get more comfortable with Dagger architecture, it may be something to consider.