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

Что такое @Provides?

1.7 Middle🔥 211 комментариев
#Dependency Injection#Архитектура и паттерны

Комментарии (1)

🐱
deepseek-v3.2PrepBro AI5 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Что такое @Provides в контексте Dagger и Hilt?

@Provides — это аннотация в библиотеках внедрения зависимостей Dagger и Hilt, которая помечает метод в классе модуля (помеченного @Module), указывая, что этот метод предоставляет (provides) объект определённого типа для системы внедрения зависимостей.

Основная цель и принцип работы

В Dagger/Hilt основным механизмом "знания" о том, как создавать объекты, является аннотация @Inject на конструкторе класса. Однако, этот подход не работает в случаях, когда:

  • Класс принадлежит сторонней библиотеке (вы не можете добавить @Inject в его код).
  • Объект нужно создавать не через конструктор, а, например, через фабричный метод (BitmapFactory.decodeFile()).
  • Зависимость является интерфейсом, и нужно указать, какую именно реализацию использовать.
  • Требуется настроить объект (например, задать конкретный экземпляр OkHttpClient с определёнными таймаутами и перехватчиками).

Для решения этих задач используются модули (@Module). Модуль — это класс, методы которого выступают в роли "фабрик" или "поставщиков" объектов. Аннотация @Provides как раз и сообщает Dagger: "Этот метод — тот самый поставщик, используй его, когда потребуется объект типа, который метод возвращает".

Ключевые аспекты использования @Provides

1. Связь с @Module

Метод с @Provides всегда должен находиться внутри класса, аннотированного @Module. Без модуля Dagger проигнорирует эту аннотацию.

@Module
object NetworkModule { // Модуль обычно объявляется как object, если не требует состояния

    @Provides
    fun provideOkHttpClient(): OkHttpClient {
        return OkHttpClient.Builder()
            .connectTimeout(30, TimeUnit.SECONDS)
            .readTimeout(30, TimeUnit.SECONDS)
            .build()
    }
}

2. Разрешение зависимостей в параметрах метода

Сила @Provides в том, что сам метод-поставщик может иметь параметры. Dagger автоматически разрешит эти параметры как зависимости и передаст их при вызове метода. Это позволяет строить граф зависимостей.

@Module
@InstallIn(SingletonComponent::class) // Аннотация Hilt для определения области видимости модуля
object StorageModule {

    // Предоставляет контекст приложения. В Hilt это делается через @ApplicationContext.
    @Provides
    fun provideSharedPreferences(
        @ApplicationContext context: Context
    ): SharedPreferences {
        // Dagger автоматически предоставит Context
        return context.getSharedPreferences("app_prefs", Context.MODE_PRIVATE)
    }
}

3. Указание конкретной реализации для интерфейса (или супертипа)

Одна из самых частых задач @Provides — сказать Dagger, какой конкретный объект возвращать, когда требуется интерфейс.

// Интерфейс репозитория
interface UserRepository {
    fun getUsers(): List<User>
}

// Конкретная реализация
class UserRepositoryImpl @Inject constructor(
    private val apiService: ApiService
) : UserRepository { ... }

// Модуль, который связывает интерфейс с реализацией
@Module
@InstallIn(ActivityComponent::class)
abstract class RepositoryModule {

    // Этот метод говорит: "Когда потребуется UserRepository, используй UserRepositoryImpl"
    // Обратите внимание на использование @Binds для более эффективной привязки.
    // @Binds — это более легковесная альтернатива @Provides для таких случаев.
    @Binds
    abstract fun bindUserRepository(impl: UserRepositoryImpl): UserRepository
}

// Аналогично с @Provides это выглядело бы так:
@Module
@InstallIn(ActivityComponent::class)
object RepositoryProvidesModule {

    @Provides
    fun provideUserRepository(apiService: ApiService): UserRepository {
        // Мы явно создаём реализацию здесь
        return UserRepositoryImpl(apiService)
    }
}

4. Работа со сторонними библиотеками

Классический пример — предоставление экземпляра Retrofit или Gson.

@Module
@InstallIn(SingletonComponent::class)
object NetworkModule {

    @Provides
    fun provideGson(): Gson {
        return GsonBuilder()
            .setDateFormat("yyyy-MM-dd'T'HH:mm:ss")
            .create()
    }

    @Provides
    fun provideRetrofit(okHttpClient: OkHttpClient, gson: Gson): Retrofit {
        return Retrofit.Builder()
            .client(okHttpClient) // Зависимость, предоставленная другим @Provides методом
            .baseUrl("https://api.example.com/")
            .addConverterFactory(GsonConverterFactory.create(gson)) // Зависимость от Gson
            .build()
    }

    @Provides
    fun provideApiService(retrofit: Retrofit): ApiService {
        return retrofit.create(ApiService::class.java)
    }
}

5. Qualifiers (Квалификаторы) и именованные зависимости

Когда для одного типа нужно предоставить разные реализации (например, два разных OkHttpClient: для основного API и для платежного), используются квалификаторы — пользовательские аннотации.

// 1. Объявляем квалификаторы
@Qualifier
@Retention(AnnotationRetention.BINARY)
annotation class AuthInterceptorClient

@Qualifier
@Retention(AnnotationRetention.BINARY)
annotation class LoggingInterceptorClient

@Module
@InstallIn(SingletonComponent::class)
object QualifierModule {

    @Provides
    @AuthInterceptorClient
    fun provideAuthOkHttpClient(authInterceptor: AuthInterceptor): OkHttpClient {
        return OkHttpClient.Builder()
            .addInterceptor(authInterceptor)
            .build()
    }

    @Provides
    @LoggingInterceptorClient
    fun provideLoggingOkHttpClient(loggingInterceptor: HttpLoggingInterceptor): OkHttpClient {
        return OkHttpClient.Builder()
            .addInterceptor(loggingInterceptor)
            .build()
    }
}

// 2. Используем в классе
class PaymentService @Inject constructor(
    @AuthInterceptorClient private val client: OkHttpClient // Будет внедрён клиент с AuthInterceptor
)

Отличие от @Binds

Как было показано в примере с интерфейсом, @Binds — это оптимизированная аннотация для случаев, когда метод модуля просто возвращает один из своих параметров (обычно реализацию интерфейса). @Binds должен быть abstract и находиться в abstract классе модуля. Он более эффективен, так как не требует написания тела метода, и Dagger генерирует более оптимальный код. @Provides же используется, когда логика создания объекта нетривиальна и требует вызовов конструкторов, фабричных методов или конфигурации.

Заключение

@Provides — это фундаментальный инструмент в арсенале Dagger/Hilt для явного определения правил создания объектов, которые не могут быть созданы стандартным путём через @Inject конструктор. Он обеспечивает гибкость, позволяя интегрировать сторонние библиотеки, настраивать объекты, предоставлять реализации для абстракций и управлять жизненным циклом зависимостей в рамках определённых компонентов (через @InstallIn в Hilt). Понимание работы @Provides и его взаимодействия с модулями, квалификаторами и графом зависимостей является обязательным для эффективного использования современных библиотек внедрения зависимостей на Android.