Настройка Dependency Injection (Hilt) в Android-приложении
Hilt — официальная DI-библиотека для Android от Google, построенная поверх Dagger 2. Убирает 80% бойлерплейта Dagger, предоставляя готовые компоненты для стандартных Android-классов. Для большинства новых проектов — разумный выбор по умолчанию.
Базовая настройка
// build.gradle.kts (project)
plugins {
id("com.google.dagger.hilt.android") version "2.51" apply false
}
// build.gradle.kts (app)
plugins {
id("com.google.dagger.hilt.android")
id("com.google.devtools.ksp")
}
dependencies {
implementation("com.google.dagger:hilt-android:2.51")
ksp("com.google.dagger:hilt-android-compiler:2.51")
}
@HiltAndroidApp на Application — точка входа, без неё Hilt не инициализируется:
@HiltAndroidApp
class App : Application()
Инжекция в Android-классы
Hilt знает о Activity, Fragment, Service, BroadcastReceiver, ViewModel и WorkManager. Для каждого — свой скоуп:
@AndroidEntryPoint
class ProfileFragment : Fragment() {
@Inject lateinit var userRepository: UserRepository
private val viewModel: ProfileViewModel by viewModels()
}
@HiltViewModel
class ProfileViewModel @Inject constructor(
private val userRepository: UserRepository,
private val analyticsService: AnalyticsService
) : ViewModel()
@AndroidEntryPoint генерирует сабкомпонент Dagger для этого класса. @HiltViewModel интегрирует ViewModel с Hilt без ручной ViewModelFactory.
Модули и биндинги
@Module
@InstallIn(SingletonComponent::class)
object NetworkModule {
@Provides
@Singleton
fun provideOkHttpClient(): OkHttpClient = OkHttpClient.Builder()
.addInterceptor(HttpLoggingInterceptor().apply {
level = if (BuildConfig.DEBUG) HttpLoggingInterceptor.Level.BODY
else HttpLoggingInterceptor.Level.NONE
})
.build()
@Provides
@Singleton
fun provideApiService(client: OkHttpClient): ApiService =
Retrofit.Builder()
.baseUrl(BuildConfig.API_URL)
.client(client)
.addConverterFactory(GsonConverterFactory.create())
.build()
.create(ApiService::class.java)
}
@Module
@InstallIn(SingletonComponent::class)
abstract class RepositoryModule {
@Binds
abstract fun bindUserRepository(impl: UserRepositoryImpl): UserRepository
}
@InstallIn(SingletonComponent::class) привязывает модуль к AppComponent Hilt. Для зависимостей уровня Activity — ActivityComponent::class, для Fragment — FragmentComponent::class.
Квалификаторы и множественные биндинги
@Qualifier
@Retention(AnnotationRetention.BINARY)
annotation class AuthenticatedClient
@Qualifier
@Retention(AnnotationRetention.BINARY)
annotation class UnauthenticatedClient
@Module
@InstallIn(SingletonComponent::class)
object HttpModule {
@Provides @Singleton @AuthenticatedClient
fun provideAuthenticatedClient(authInterceptor: AuthInterceptor): OkHttpClient =
OkHttpClient.Builder().addInterceptor(authInterceptor).build()
@Provides @Singleton @UnauthenticatedClient
fun provideUnauthenticatedClient(): OkHttpClient = OkHttpClient.Builder().build()
}
Без квалификаторов Hilt не может различить два OkHttpClient — ошибка компиляции [Dagger/DuplicateBindings].
Тестирование с Hilt
@HiltAndroidTest
@RunWith(AndroidJUnit4::class)
class ProfileFragmentTest {
@get:Rule
val hiltRule = HiltAndroidRule(this)
@BindValue
@JvmField
val fakeRepository: UserRepository = FakeUserRepository()
@Test
fun displaysUserName() {
// тест
}
}
@BindValue заменяет реальный биндинг на фейк прямо в тесте, без создания тестового модуля. Для юнит-тестов ViewModels Hilt не нужен — зависимости передаются в конструктор напрямую.
Частая ошибка: @Inject в non-Android классах без @AndroidEntryPoint
@Inject в Fragment без @AndroidEntryPoint даёт NullPointerException при обращении к заинжектированному полю — поле остаётся null. Hilt не инжектирует в классы, на которых нет его аннотации. Типичный краш при копировании Fragment из старого кода в новый проект.
Настройка Hilt с нуля в новом проекте — 1-2 дня. Миграция с ручного DI или Dagger 2 — 3-7 дней в зависимости от размера проекта. Стоимость рассчитывается индивидуально.







