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 замінює реальний біндинг на фейк безпосередньо в тесті, без створення тестового модуля. Для unit-тестів ViewModels Hilt не потрібен — залежності передаються в конструктор безпосередньо.
Частиба помилка: @Inject у non-Android класах без @AndroidEntryPoint
@Inject в Fragment без @AndroidEntryPoint дає NullPointerException при зверненню до заінжектованого поля — поле залишається null. Hilt не інжектує в класи, на яких немає його анотації. Типовий крах при копіюванні Fragment зі старого коду в новий проект.
Налаштування Hilt з нуля в новому проекті — 1–2 дні. Міграція з ручного DI або Dagger 2 — 3–7 днів залежно від розміру проекту. Вартість розраховується індивідуально.







