Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI23 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Dagger 2: Dependency Injection для Android
Dagger — это фреймворк для автоматической инъекции зависимостей (DI) на Android. Он позволяет управлять зависимостями между объектами, делая код более тестируемым и поддерживаемым.
Проблема без Dagger
Ручное создание зависимостей
class UserViewModel(
private val userRepository: UserRepository,
private val analyticsService: AnalyticsService,
private val networkService: NetworkService,
private val cacheService: CacheService
) : ViewModel() { }
// Когда нужна ViewModel в Activity
val apiService = ApiService()
val database = AppDatabase.getDatabase(context)
val userRepository = UserRepository(apiService, database)
val analytics = AnalyticsService()
val networkService = NetworkService()
val cache = CacheService()
val viewModel = UserViewModel(userRepository, analytics, networkService, cache)
Проблемы:
- Много boilerplate кода
- Сложно менять реализацию
- Тяжело тестировать (нужны моки)
- Если изменить конструктор — изменений везде
Решение: Dagger
Dagger автоматизирует создание объектов:
// 1. Определяем модули (фабрики)
@Module
@InstallIn(SingletonComponent::class)
class AppModule {
@Provides
@Singleton
fun provideApiService(): ApiService {
return ApiService()
}
@Provides
@Singleton
fun provideDatabase(@ApplicationContext context: Context): AppDatabase {
return Room.databaseBuilder(context, AppDatabase::class.java, "app.db")
.build()
}
@Provides
@Singleton
fun provideUserRepository(
apiService: ApiService,
database: AppDatabase
): UserRepository {
return UserRepository(apiService, database)
}
}
// 2. Тогда в ViewModel просто запрашиваем
class UserViewModel @Inject constructor(
private val userRepository: UserRepository,
private val analyticsService: AnalyticsService
) : ViewModel() { }
// 3. А в Activity автоматически внедряется
@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
private val viewModel: UserViewModel by viewModels()
// viewModel уже готов!
}
Как работает Dagger
Шаг 1: Аннотации
// @Inject — просим Dagger внедрить
class UserRepository @Inject constructor(
private val apiService: ApiService,
private val database: AppDatabase
) { }
// Dagger видит зависимости и ищет провайдеры
Шаг 2: Модули
// @Module — контейнер провайдеров
// @Provides — фабрика для объекта
@Module
@InstallIn(SingletonComponent::class)
class NetworkModule {
@Provides
@Singleton // Только один экземпляр
fun provideRetrofit(): Retrofit {
return Retrofit.Builder()
.baseUrl("https://api.example.com")
.addConverterFactory(GsonConverterFactory.create())
.build()
}
}
Шаг 3: Компоненты
// Приложение (инициализирует Dagger)
@HiltAndroidApp
class MyApplication : Application() {
// Dagger создаёт граф зависимостей
}
Основные концепции
1. Scopes (Жизненные циклы)
// @Singleton — один объект на всё приложение
@Provides
@Singleton
fun provideDatabase(): AppDatabase { }
// @ActivityScoped — один на Activity
@Provides
@ActivityScoped
fun provideUserPresenter(): UserPresenter { }
// Без scope — новый объект каждый раз (Transient)
@Provides
fun provideUserFactory(): UserFactory { }
2. Qualifiers (Различение реализаций)
// Когда есть несколько реализаций одного интерфейса
@Qualifier
@Retention(AnnotationRetention.BINARY)
annotation class MainDatabase
@Qualifier
@Retention(AnnotationRetention.BINARY)
annotation class CacheDatabase
@Module
class DatabaseModule {
@Provides
@Singleton
@MainDatabase
fun provideMainDb(): Database { }
@Provides
@Singleton
@CacheDatabase
fun provideCacheDb(): Database { }
}
// Использование
class Repository @Inject constructor(
@MainDatabase private val mainDb: Database,
@CacheDatabase private val cacheDb: Database
) { }
3. Hilt (Современный Dagger)
Hilt упрощает Dagger для Android:
@HiltAndroidApp
class MyApplication : Application()
@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
private val viewModel: UserViewModel by viewModels()
}
@AndroidEntryPoint
class UserFragment : Fragment() {
private val viewModel: UserViewModel by viewModels()
}
Примеры использования
Пример 1: REST API
@Module
@InstallIn(SingletonComponent::class)
class ApiModule {
@Provides
@Singleton
fun provideOkHttpClient(): OkHttpClient {
return OkHttpClient.Builder()
.addInterceptor(LoggingInterceptor())
.readTimeout(30, TimeUnit.SECONDS)
.build()
}
@Provides
@Singleton
fun provideRetrofit(okHttp: OkHttpClient): Retrofit {
return Retrofit.Builder()
.baseUrl("https://api.example.com")
.client(okHttp)
.addConverterFactory(GsonConverterFactory.create())
.build()
}
@Provides
@Singleton
fun provideUserService(retrofit: Retrofit): UserService {
return retrofit.create(UserService::class.java)
}
}
class UserRepository @Inject constructor(
private val userService: UserService
) { }
Пример 2: База данных
@Module
@InstallIn(SingletonComponent::class)
class DatabaseModule {
@Provides
@Singleton
fun provideDatabase(
@ApplicationContext context: Context
): AppDatabase {
return Room.databaseBuilder(
context,
AppDatabase::class.java,
"app_database"
).build()
}
@Provides
@Singleton
fun provideUserDao(db: AppDatabase): UserDao {
return db.userDao()
}
}
Пример 3: Репозиторий
class UserRepository @Inject constructor(
private val userService: UserService,
private val userDao: UserDao
) {
suspend fun getUsers(): List<User> {
return try {
userService.getUsers().also { users ->
userDao.insertAll(users) // Кеш
}
} catch (e: Exception) {
userDao.getAllUsers() // Из кеша
}
}
}
Пример 4: ViewModel
class UserViewModel @Inject constructor(
private val repository: UserRepository
) : ViewModel() {
private val _users = MutableStateFlow<List<User>>(emptyList())
val users: StateFlow<List<User>> = _users.asStateFlow()
fun loadUsers() {
viewModelScope.launch {
_users.value = repository.getUsers()
}
}
}
Тестирование с Dagger
// Тестовый модуль
@Module
@InstallIn(SingletonComponent::class)
class TestAppModule {
@Provides
@Singleton
fun provideUserRepository(): UserRepository {
return FakeUserRepository() // Mock!
}
}
// Тест
@RunWith(AndroidJUnit4::class)
class UserViewModelTest {
@get:Rule
val hiltRule = HiltAndroidRule(this)
@Inject
lateinit var viewModel: UserViewModel
@Before
fun setup() {
hiltRule.inject()
}
@Test
fun testLoadUsers() {
viewModel.loadUsers()
assertEquals(expected, viewModel.users.value)
}
}
Плюсы и минусы
Плюсы
- ✅ Меньше boilerplate кода
- ✅ Легче менять реализацию
- ✅ Улучшена тестируемость
- ✅ Проверка зависимостей на compile-time
- ✅ Производительность (всё в compile-time)
Минусы
- ❌ Кривая обучения
- ❌ Увеличивает время компиляции
- ❌ Много аннотаций и конфигурации
- ❌ Сложнее дебагировать
Best Practices
- Используй Hilt вместо чистого Dagger
- Разделяй модули по функциям: ApiModule, DatabaseModule, ServiceModule
- Используй Qualifiers для различения реализаций
- Правильно выбирай Scopes: Singleton, ActivityScoped, ViewModelScoped
- Тестируй с тестовыми модулями
- Не создавай граф зависимостей слишком сложным
Вывод: Dagger (или Hilt) — это стандарт для управления зависимостями в современных Android приложениях. Он делает код чище, проще для тестирования и более поддерживаемым.