Как работает Dagger?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Как работает Dagger
Что такое Dagger
Dagger — фреймворк для Dependency Injection (внедрения зависимостей) в Java/Kotlin. Он генерирует код на этапе компиляции, что делает его невероятно быстрым на runtime (в отличие от reflection-based DI фреймворков).
Для Android используется Dagger 2 (или Hilt — обертка над Dagger 2).
Основные концепции
1. Component
Component — это контейнер, который знает, как создавать экземпляры зависимостей.
@Component(modules = [AppModule::class])
interface AppComponent {
fun inject(activity: MainActivity)
}
Это интерфейс, и Dagger сгенерирует реализацию (DaggerAppComponent).
2. Module
Module — это класс, содержащий провайдеры для создания зависимостей.
@Module
class AppModule {
@Provides
@Singleton
fun provideRepository(): UserRepository {
return UserRepository()
}
@Provides
fun provideService(repository: UserRepository): UserService {
return UserService(repository)
}
}
Dagger сканирует методы с @Provides и автоматически создает граф зависимостей.
3. Scope
Scope определяет жизненный цикл объекта:
- @Singleton — один экземпляр на все приложение
- @ActivityScope — один экземпляр на Activity
- Без аннотации — новый экземпляр каждый раз
@Module
class DatabaseModule {
@Provides
@Singleton // Один Database на все приложение
fun provideDatabase(): AppDatabase {
return Room.databaseBuilder(context, AppDatabase::class.java, "db").build()
}
}
Как это работает шаг за шагом
Шаг 1: Определяешь зависимости
class UserRepository(val apiClient: ApiClient)
class UserService(val repository: UserRepository)
Шаг 2: Создаешь Module с провайдерами
@Module
class AppModule {
@Provides
@Singleton
fun provideApiClient(): ApiClient = Retrofit.Builder()
.baseUrl("https://api.example.com")
.build()
.create(ApiClient::class.java)
@Provides
@Singleton
fun provideRepository(apiClient: ApiClient): UserRepository {
return UserRepository(apiClient)
}
@Provides
fun provideService(repository: UserRepository): UserService {
return UserService(repository)
}
}
Шаг 3: Создаешь Component
@Component(modules = [AppModule::class])
interface AppComponent {
fun inject(activity: MainActivity)
}
Шаг 4: Dagger генерирует код
После компиляции Dagger создает класс DaggerAppComponent, который знает, как собрать граф:
// Сгенерировано Dagger'ом
class DaggerAppComponent : AppComponent {
private val apiClient: ApiClient = provideApiClient()
private val repository: UserRepository = provideRepository(apiClient)
override fun inject(activity: MainActivity) {
activity.service = provideService(repository)
}
}
Шаг 5: Используешь в коде
class MainActivity : AppCompatActivity() {
@Inject
lateinit var userService: UserService
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val component = DaggerAppComponent.create()
component.inject(this)
// userService готов к использованию!
userService.loadUsers()
}
}
Субкомпоненты для Scoping
@ActivityScope
@Subcomponent(modules = [ActivityModule::class])
interface ActivityComponent {
fun inject(activity: MainActivity)
}
@Module(subcomponents = [ActivityComponent::class])
class AppModule
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
val activityComponent = (app.appComponent as AppComponent)
.activityComponent()
.create()
activityComponent.inject(this)
}
}
Dagger Hilt (рекомендуется для Android)
Hilt — обертка над Dagger 2, которая автоматизирует много боллерплейта:
@Module
@InstallIn(SingletonComponent::class) // Вместо @Component
class AppModule {
@Provides
@Singleton
fun provideRepository(): UserRepository = UserRepository()
}
@HiltAndroidApp // В Application класс
class MyApp : Application()
@AndroidEntryPoint // В Activity/Fragment
class MainActivity : AppCompatActivity() {
@Inject
lateinit var repository: UserRepository
}
Преимущества Dagger
✅ Компиляция вместо runtime — код генерируется при компиляции, нет reflection
✅ Граф зависимостей проверяется на компиляции — ошибки не упустишь
✅ Высокая производительность — нет reflection overhead
✅ Явность — все зависимости видны в коде
✅ Scoping — полный контроль над жизненным циклом объектов
Недостатки
❌ Боллерплейт — много кода для маленьких проектов
❌ Кривая обучения — сложный концептуально
❌ Медленная компиляция — генерация кода замедляет сборку
Практический пример: приложение с Database
@Module
@InstallIn(SingletonComponent::class)
class DatabaseModule {
@Provides
@Singleton
fun provideDatabase(@ApplicationContext context: Context): AppDatabase {
return Room.databaseBuilder(context, AppDatabase::class.java, "app_db")
.build()
}
@Provides
fun provideUserDao(db: AppDatabase): UserDao = db.userDao()
}
@Module
@InstallIn(SingletonComponent::class)
class RepositoryModule {
@Provides
fun provideUserRepository(dao: UserDao): UserRepository {
return UserRepository(dao)
}
}
@HiltAndroidApp
class MyApp : Application()
@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
@Inject
lateinit var repository: UserRepository
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
repository.getUsers() // Готов!
}
}
Итог
Dagger работает в три этапа:
- Компиляция — сканирует @Module/@Provides/Component
- Генерация — создает граф зависимостей и классы (DaggerXxx)
- Runtime — просто вызывает сгенерированный код, нет reflection
Для современного Android используй Hilt — это официально рекомендуемый подход, который убирает половину боллерплейта.