Научиться проектировать DI-граф в Android-приложении с помощью Dagger 2
- Переведите приложение с паттерна Service Locator на DI, используя Dagger 2.
- Для одной фичи используйте подход Component Dependencies, для второй — Subcomponent
Приложение Products с двумя экранами:
- Products Screen - список продуктов
- Favorites Screen - список избранных продуктов
Технологии:
- Jetpack Compose для UI
- Navigation Compose для навигации
- Retrofit для загрузки данных
- DataStore для хранения избранного
- Clean Architecture
- Dagger 2 для DI
Главный компонент приложения с областью видимости @Singleton.
Что должен предоставлять:
- Сетевые зависимости (Retrofit, OkHttp, ProductApiService)
- Репозитории (ProductRepository, FavoritesRepository)
- Use Cases (ConsumeProductsUseCase, ConsumeFavoritesUseCase, ToggleFavoriteUseCase)
- Утилиты (PriceFormatter)
- Context приложения
Подсказки:
- Используйте
@BindsInstanceдля передачиContextв компонент - AppComponent реализует только ProductsDependencies (не FavoritesDependencies!)
- Для Favorites используется метод
favoritesComponent()который возвращает фабрику сабкомпонента - Не забудьте добавить SubcomponentsModule в список модулей
- Создайте компонент в классе Application
Для фичи Products сделайте компонент через Component Dependencies
@FeatureScope
@Component(dependencies = [ProductsDependencies::class])
interface ProductsComponent {
fun viewModelFactory(): ProductsViewModelFactory
@Component.Factory
interface Factory {
fun create(dependencies: ProductsDependencies): ProductsComponent
}
}Для фичи Favorites сделайте компонент через Subcomponent
@FeatureScope
@Subcomponent
interface FavoritesComponent {
fun viewModelFactory(): FavoritesViewModelFactory
@Subcomponent.Factory
interface Factory {
fun create(): FavoritesComponent
}
}Подсказки:
- Используйте собственную аннотацию
@FeatureScopeв модулеcommon:di - Products использует Component Dependencies (через интерфейс)
- Favorites использует Subcomponent (прямая зависимость от AppComponent)
- Каждый компонент предоставляет только ViewModelFactory для своей фичи
- Базовый интерфейс
Dependenciesдолжен быть пустым маркером - Объявляйте только минимально необходимые зависимости
- AppComponent реализует интерфейс ProductsDependencies
- Получайте зависимости, например, через
findDependencies<ProductsDependencies>(), как показано ниже:
interface Dependencies
interface DependenciesProvider {
fun getDependencies(): Dependencies
}
inline fun <reified T : Dependencies> Context.findDependencies(): T {
return (applicationContext as DependenciesProvider).getDependencies() as T
}Это один из способов. Можете сделать иначе.
- Не передавайте весь компонент
// Плохо
@Component(dependencies = [AppComponent::class])
// Хорошо
@Component(dependencies = [FeatureDependencies::class])- Не используйте скоуп для stateless классов
// Плохо - use case без состояния
@Singleton
class MyUseCase @Inject constructor(...)
// Хорошо
class MyUseCase @Inject constructor(...)Удачи в освоении Dependency Injection! 🚀

