← Назад к вопросам

Для чего нужен Dagger?

1.0 Junior🔥 301 комментариев
#Dependency Injection

Комментарии (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

  1. Используй Hilt вместо чистого Dagger
  2. Разделяй модули по функциям: ApiModule, DatabaseModule, ServiceModule
  3. Используй Qualifiers для различения реализаций
  4. Правильно выбирай Scopes: Singleton, ActivityScoped, ViewModelScoped
  5. Тестируй с тестовыми модулями
  6. Не создавай граф зависимостей слишком сложным

Вывод: Dagger (или Hilt) — это стандарт для управления зависимостями в современных Android приложениях. Он делает код чище, проще для тестирования и более поддерживаемым.

Для чего нужен Dagger? | PrepBro