Как внедрять зависимости в Android-проектах на Kotlin (например, с Hilt)?

Внедрение зависимостей (Dependency Injection, DI) — это подход, при котором зависимости (например, репозитории, сетевые клиенты, базы данных) предоставляются объектам извне, а не создаются внутри самих объектов. Это упрощает модульность, тестируемость и масштабируемость Android-приложений. В Android на языке Kotlin наиболее популярным инструментом для внедрения зависимостей является Hilt — официальный DI-фреймворк от Google, построенный поверх Dagger.

Что такое Hilt

Hilt — это библиотека для внедрения зависимостей, предназначенная упростить и стандартизировать DI в Android. Она интегрируется с жизненным циклом Android-компонентов (Activity, Fragment, ViewModel, Service и др.), снижает количество шаблонного кода и позволяет легко управлять графом зависимостей.

Как работает DI в Hilt

  • Используется аннотация @Inject для указания, как создавать экземпляры.

  • Определяются модули (@Module) и способы предоставления зависимостей (@Provides, @Binds).

  • Указывается область действия (@Singleton, @ActivityScoped, @ViewModelScoped и т.д.).

  • Компоненты (Activity, Fragment и др.) аннотируются @AndroidEntryPoint, чтобы Hilt мог внедрить зависимости.

Шаги настройки Hilt в Android-проекте

1. Добавление зависимостей в build.gradle

// project-level build.gradle
classpath "com.google.dagger:hilt-android-gradle-plugin:2.50"
// app-level build.gradle
plugins {
id 'kotlin-kapt'
id 'dagger.hilt.android.plugin'
}
dependencies {
implementation "com.google.dagger:hilt-android:2.50"
kapt "com.google.dagger:hilt-compiler:2.50"
}

2. Инициализация Hilt в Application

Создаётся класс MyApplication, аннотированный @HiltAndroidApp:

@HiltAndroidApp
class MyApplication : Application()
AndroidManifest.xml:
<application
android:name=".MyApplication"
...>
&lt;/application&gt;

3. Аннотирование Android-компонентов

Чтобы Hilt внедрил зависимости, аннотируй компонент:

@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
@Inject lateinit var repository: UserRepository
}

Для Fragment, Service, BroadcastReceiver, ViewModel — также использовать @AndroidEntryPoint.

4. Внедрение в ViewModel

@HiltViewModel
class MainViewModel @Inject constructor(
private val repository: UserRepository
) : ViewModel() {
...
}

Использование в UI:

val viewModel: MainViewModel by viewModels()

Примеры создания зависимостей

A. Автоматическое создание зависимостей с @Inject constructor

class NetworkService @Inject constructor() {
fun request() = "Ответ с сервера"
}
class UserRepository @Inject constructor(
private val networkService: NetworkService
)

B. Создание через @Module и @Provides

Если нельзя добавить @Inject в конструктор (например, для сторонних библиотек):

@Module
@InstallIn(SingletonComponent::class)
object AppModule
@Provides
@Singleton
fun provideOkHttpClient(): OkHttpClient = OkHttpClient.Builder().build()
@Provides
@Singleton
fun provideRetrofit(client: OkHttpClient): Retrofit =
Retrofit.Builder()
.baseUrl("https://example.com")
.client(client)
.build()
}

C. Интерфейсы с @Binds

Если нужно связать интерфейс с реализацией:

interface AuthService {
fun login()
}
class AuthServiceImpl @Inject constructor() : AuthService {
override fun login() { ... }
}
@Module
@InstallIn(SingletonComponent::class)
abstract class AuthModule {
@Binds
abstract fun bindAuthService(
impl: AuthServiceImpl
): AuthService
}

Области действия зависимостей

Hilt позволяет управлять временем жизни объектов с помощью скоупов:

Скоуп Описание
@Singleton Один экземпляр на всё приложение
--- ---
@ActivityRetainedScoped Один экземпляр на Activity, сохраняется при конфигурационных изменениях
--- ---
@ActivityScoped Один экземпляр на Activity
--- ---
@ViewModelScoped Один экземпляр на ViewModel
--- ---
@FragmentScoped Один экземпляр на Fragment
--- ---

Пример:

@Provides
@ActivityScoped
fun provideSessionManager(): SessionManager = SessionManager()

Тестирование зависимостей с Hilt

Для замены зависимостей в тестах используется @TestInstallIn:

@Module
@TestInstallIn(
components = \[SingletonComponent::class\],
replaces = \[AppModule::class\]
)
object TestModule {
@Provides
fun provideFakeRepo(): UserRepository = FakeUserRepository()
}

Также можно использовать @HiltAndroidTest и HiltAndroidRule.

Дополнительно

Инъекция в WorkManager

@HiltWorker
class SyncWorker @AssistedInject constructor(
@Assisted context: Context,
@Assisted workerParams: WorkerParameters,
private val repository: SyncRepository
) : CoroutineWorker(context, workerParams) {
override suspend fun doWork(): Result {
repository.sync()
return Result.success()
}
}

Инъекция в Navigation и Compose

@Composable
fun HomeScreen(
viewModel: HomeViewModel = hiltViewModel()
) {
...
}

Используемые аннотации в Hilt

Аннотация Назначение
@HiltAndroidApp Инициализация DI-графа в Application
--- ---
@AndroidEntryPoint Помечает компонент, куда Hilt может внедрить зависимости
--- ---
@Inject Обозначает, где и что должно быть внедрено
--- ---
@Module Определяет модуль с зависимостями
--- ---
@Provides Указывает способ предоставления зависимости
--- ---
@Binds Привязка реализации к интерфейсу
--- ---
@InstallIn Указывает, в какой компонент будет установлен модуль
--- ---
@Singleton, @Scoped Управляют временем жизни зависимости
--- ---
@HiltViewModel Обозначает ViewModel для DI
--- ---
@AssistedInject Используется в Worker и других классах с Assisted-инжекцией
--- ---