Как внедрять зависимости в 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"
...>
</application>
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-инжекцией |
--- | --- |