Let’s create a ScreensNavigator interface:
interface ScreensNavigator { fun navigateBack() fun toQuestionDetails(questionId: String) } And ScreensNavigatorImpl implements it:
class ScreensNavigatorImpl @Inject constructor(private val activity: AppCompatActivity) : ScreensNavigator { override fun navigateBack() { activity.onBackPressed() } override fun toQuestionDetails(questionId: String) { QuestionDetailsActivity.start(activity, questionId) } } Now we have a problem that in activities and fragments we want to inject the interface type:
@Inject lateinit var screensNavigator: ScreensNavigator But Dagger only knows how to create ScreensNavigatorImpl.
One fact: it is more performant to make the all provider methods inside Modules static.
But for Modules that have bootstrap dependencies, this is impossible. Let’s take ActivityModule as example.
@Module class ActivityModule(private val activity: AppCompatActivity) { @Provides fun activity(): AppCompatActivity = activity @Provides fun layoutInflater(activity: AppCompatActivity): LayoutInflater = LayoutInflater.from(activity) @Provides fun fragmentManager(activity: AppCompatActivity) = activity.supportFragmentManager @Provides @ActivityScope fun screensNavigator(activity: AppCompatActivity) = ScreensNavigator(activity) } We can easily make fun layoutInflater(), fragmentManager() and screensNavigator static methods.
Components can have multiple Modules. So if you have huge Modules that provide tons of services, it is good to split it up.
Let’s create a UseCaseModule to hanle all the UseCases.
@Module class UseCasesModule { @Provides fun fetchQuestionsUseCase(stackOverflowApi: StackoverflowApi) = FetchQuestionsUseCase(stackOverflowApi) @Provides fun fetchQuestionDetailsUseCase(stackOverflowApi: StackoverflowApi) = FetchQuestionDetailsUseCase(stackOverflowApi) } Some dagger conventions:
Components can use more than 1 module. Modules of the same Component share the same object-graph. Dagger automatically instantiate modules with no argument constructor.
In previous post, we introduced the component dependencies. But there is one interesting subtlety: If Component B depends on Component A, B has access to all A’s exposed objects. Note that there is a difference between what a component exposes and what it provides.
Let’s look at PresentationComponent and ActivityComponent.
@PresentationScope @Component(dependencies = [ActivityComponent::class], modules = [PresentationModule::class]) interface PresentationComponent { fun inject(fragment: QuestionsListFragment) fun inject(questionDetailsActivity: QuestionDetailsActivity) } Here in PresentationComponent, even though it provides all the objects that QuestionsListFragment and QuestionDetailsActivity need, it exposes nothing.
Let’s look at the PresentationComponent more closely.
One strange thing is that PresentationModule has ActivityComponent as its dependency, and it @Provide all the objects provided by ActivityComponent already.
This is rather ugly, can we do something about it?
The answer is Component Dependencies.
The idea is that we can make PresentationComponent depends on ActivityComponent. That way, PresentationComponent automatically gets access to all the objects exposed by ActivityComponent.
After this change, it looks like this: