Какие знаешь аннотации в Dagger позволяющие добавить класс в граф зависимостей?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Какие знаешь аннотации в Dagger позволяющие добавить класс в граф зависимостей
В Dagger есть несколько способов добавить класс в граф зависимостей. Это основа для работы dependency injection. Расскажу о всех основных аннотациях.
1. @Inject на конструктор
Самый простой и рекомендуемый способ. Dagger автоматически использует конструктор для создания экземпляра.
class UserRepository @Inject constructor(
private val apiService: ApiService,
private val database: AppDatabase
) {
// Dagger автоматически создаст этот класс
// и внедрит зависимости
}
class MainActivity : AppCompatActivity() {
@Inject
lateinit var userRepository: UserRepository
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// DaggerActivityComponent.inject(this)
}
}
Преимущества:
- Простота и читаемость
- Dagger автоматически генерирует код
- Поддержка всех зависимостей конструктора
- Работает с Hilt out-of-the-box
2. @Provides в Module
Для создания сложных объектов или если конструктор с @Inject недостаточен.
@Module
class NetworkModule {
@Provides
@Singleton
fun provideOkHttpClient(): OkHttpClient {
return OkHttpClient.Builder()
.connectTimeout(30, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.build()
}
@Provides
@Singleton
fun provideRetrofit(okHttpClient: OkHttpClient): Retrofit {
return Retrofit.Builder()
.baseUrl("https://api.example.com/")
.client(okHttpClient)
.addConverterFactory(GsonConverterFactory.create())
.build()
}
@Provides
@Singleton
fun provideApiService(retrofit: Retrofit): ApiService {
return retrofit.create(ApiService::class.java)
}
}
Когда использовать:
- Создание конфигурированных объектов (Retrofit, OkHttp)
- Объекты из внешних библиотек
- Сложная логика инициализации
- Нужен выбор между разными реализациями
3. @Binds в Module
Для связывания interface с его реализацией. Более эффективен, чем @Provides.
interface UserRepository {
suspend fun getUser(id: Long): User
}
@Inject
class UserRepositoryImpl(
private val apiService: ApiService
) : UserRepository {
override suspend fun getUser(id: Long): User {
return apiService.fetchUser(id)
}
}
@Module
abstract class RepositoryModule {
@Binds
abstract fun bindUserRepository(
userRepositoryImpl: UserRepositoryImpl
): UserRepository
}
Преимущества:
- Один строка вместо полного @Provides метода
- Меньше код (без лишней логики)
- Более читаемо
- Dagger не генерирует дополнительный код
4. @Multibinds (для коллекций)
Для добавления нескольких реализаций в граф.
@Module
abstract class InterceptorModule {
@Binds
@IntoSet
abstract fun bindAuthInterceptor(
impl: AuthInterceptor
): Interceptor
@Binds
@IntoSet
abstract fun bindLoggingInterceptor(
impl: LoggingInterceptor
): Interceptor
}
@Module
class NetworkModule {
@Provides
fun provideOkHttpClient(
interceptors: Set<Interceptor>
): OkHttpClient {
return OkHttpClient.Builder()
.apply {
interceptors.forEach { addInterceptor(it) }
}
.build()
}
}
5. @IntoMap (для Map зависимостей)
Создание Map с разными реализациями.
@MapKey
annotation class ApiKeyQualifier(val value: String)
interface ApiFactory {
fun create(): ApiService
}
@Module
abstract class ApiModule {
@Binds
@IntoMap
@ApiKeyQualifier("v1")
abstract fun bindApiV1(impl: ApiV1): ApiFactory
@Binds
@IntoMap
@ApiKeyQualifier("v2")
abstract fun bindApiV2(impl: ApiV2): ApiFactory
}
class ApiManager @Inject constructor(
private val apiFactories: Map<String, @JvmSuppressWildcards ApiFactory>
) {
fun getApi(version: String): ApiService {
return apiFactories[version]?.create()
?: throw IllegalArgumentException("Unknown API version")
}
}
6. Qualifier аннотации
Для различения нескольких реализаций одного типа.
@Qualifier
@Retention(AnnotationRetention.RUNTIME)
annotation class LoggingEnabled
@Qualifier
@Retention(AnnotationRetention.RUNTIME)
annotation class MockApi
@Module
class ApiModule {
@Provides
@Singleton
@LoggingEnabled
fun provideLoggingApi(okHttpClient: OkHttpClient): ApiService {
return ApiService(okHttpClient, loggingEnabled = true)
}
@Provides
@MockApi
fun provideMockApi(): ApiService {
return MockApiService()
}
}
class MyViewModel @Inject constructor(
@LoggingEnabled private val api: ApiService
) {
// Получит реализацию с логированием
}
7. @BindsOptional
Для опциональных зависимостей.
@Module
abstract class AnalyticsModule {
@Binds
@BindsOptional
abstract fun bindAnalytics(impl: FirebaseAnalytics): Analytics
}
class MyViewModel @Inject constructor(
private val analytics: Optional<Analytics>
) {
fun trackEvent(name: String) {
analytics.ifPresent { it.track(name) }
}
}
8. Scope аннотации
Для управления жизненным циклом объектов.
@Scope
@Retention(AnnotationRetention.RUNTIME)
annotation class ActivityScope
@ActivityScope
@Component(modules = [ActivityModule::class])
interface ActivityComponent {
fun inject(activity: MainActivity)
}
@ActivityScope
class UserRepository @Inject constructor(
private val apiService: ApiService
) {
// Синглтон на уровне Activity
}
9. Hilt аннотации
Модернизированный Dagger с предустановленными scope.
@HiltViewModel
class MyViewModel @Inject constructor(
private val userRepository: UserRepository
) : ViewModel() {}
@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
private val viewModel: MyViewModel by viewModels()
}
Сравнение аннотаций
| Аннотация | Когда использовать | Преимущества |
|---|---|---|
| @Inject constructor | Простые классы | Простота, автоматизм |
| @Provides | Сложные объекты | Контроль, конфигурация |
| @Binds | Interface реализации | Компактно, эффективно |
| @IntoSet/@IntoMap | Множественные реализации | Гибкость, масштабируемость |
| Qualifier | Разные реализации одного типа | Различие, ясность |
| @BindsOptional | Опциональные зависимости | Безопасность |
Лучшие практики
- Используй @Inject на конструктор в первую очередь
- @Provides только для сложных объектов
- @Binds вместо @Provides для interface
- Избегай циклических зависимостей
- Используй Scopes для управления жизненным циклом
- Prefer Hilt в новых проектах (вместо чистого Dagger)
Это основные способы добавления классов в граф зависимостей Dagger. Выбор аннотации зависит от сложности инициализации и требований проекта.