Android Hilt 依賴注入框架 (Dependency Injection, DI)
在開發中大型 Android 應用程式時,手動管理物件的建立與生命週期會讓程式碼變得難以維護且難以測試。依賴注入 (Dependency Injection) 是一種設計模式,能將物件的「建立」與「使用」分離,提升解耦性。
Hilt 是 Google 專為 Android 打造的 DI 框架,它建構在強大的 Dagger 2 之上,但透過標準化的容器與註解,大幅簡化了繁瑣的配置。
安裝與環境配置
在專案級 build.gradle.kts 加入 plugin:
plugins {
alias(libs.plugins.hilt.android) apply false
}
在 App 級 build.gradle.kts 啟用 plugin 與依賴:
plugins {
alias(libs.plugins.hilt.android)
kotlin("kapt") // 或使用 ksp
}
dependencies {
implementation("com.google.dagger:hilt-android:2.51")
kapt("com.google.dagger:hilt-android-compiler:2.51")
}
核心註解:從入口點到注入
@HiltAndroidApp
所有使用 Hilt 的專案都必須在 Application 類別加上此註解,它是 Hilt 產生程式碼的起點。
@HiltAndroidApp
class MyApplication : Application()
@AndroidEntryPoint
告訴 Hilt 在這個 Android 元件(Activity, Fragment, View, Service, BroadcastReceiver)中啟用注入。
@AndroidEntryPoint
class UserActivity : ComponentActivity() {
// 欄位注入 (Field Injection)
@Inject lateinit var analytics: AnalyticsAdapter
}
@Inject (建構子注入)
這是最推薦的注入方式。它告訴 Hilt 如何建立該類別的實體,以及它需要哪些依賴。
class UserRepository @Inject constructor(
private val api: ApiService
) { ... }
Modules:處理第三方與介面綁定
當你無法在類別建構子上加上 @Inject 時(例如使用 Retrofit 或處理介面),就需要 @Module。
@Provides (針對第三方類別)
用於處理非自己編寫的類別,如資料庫或網路庫。
@Module
@InstallIn(SingletonComponent::class)
object NetworkModule {
@Provides
@Singleton
fun provideRetrofit(): Retrofit = Retrofit.Builder().baseUrl("...").build()
}
@Binds (針對介面綁定)
當你需要注入一個介面 (Interface),且實作類別已有 @Inject 時,使用 @Binds 比 @Provides 更有效率。
interface AnalyticsService { fun logEvent(name: String) }
class AnalyticsServiceImpl @Inject constructor() : AnalyticsService { ... }
@Module
@InstallIn(SingletonComponent::class)
abstract class AnalyticsModule {
@Binds
abstract fun bindAnalytics(impl: AnalyticsServiceImpl): AnalyticsService
}
限定符 (@Qualifier):處理多重實作
當同一個型別(例如 OkHttpClient)有多種不同的配置時,我們需要自定義註解來告知 Hilt 要注入哪一個。
@Qualifier
@Retention(AnnotationRetention.BINARY)
annotation class AuthOkHttpClient
@Qualifier
@Retention(AnnotationRetention.BINARY)
annotation class PublicOkHttpClient
// 在 Module 中使用
@AuthOkHttpClient
@Provides
fun provideAuthClient(): OkHttpClient = ...
// 在注入點使用
class MyRepo @Inject constructor(
@AuthOkHttpClient private val client: OkHttpClient
)
注入 Context
Hilt 內建了常用的 Context 綁定,無需自己寫 Module。
class MyManager @Inject constructor(
@ApplicationContext private val context: Context
)
ViewModel 與進階注入
@HiltViewModel
Hilt 能自動處理 ViewModel 的注入,並提供生命週期支援。
@HiltViewModel
class UserViewModel @Inject constructor(
private val repository: UserRepository,
private val savedStateHandle: SavedStateHandle // 自動生成的狀態保存器
) : ViewModel() { ... }
輔助注入 (Assisted Injection)
如果你需要在運行時才決定某些參數(如從 Intent 傳來的 ID),應使用輔助注入。
class DetailViewModel @AssistedInject constructor(
private val repo: Repo,
@Assisted private val userId: String // 運行時才傳入的輔助參數
) : ViewModel() { ... }
生命週期與作用域 (Scope)
Hilt 的物件存活時間取決於它被安裝在在哪個 Component 中。
| Component | 作用域 (Scope) | 生命週期 |
|---|---|---|
| SingletonComponent | @Singleton | 全 App 只有一個 (Application) |
| ActivityRetainedComponent | @ActivityRetainedScoped | 跨螢幕旋轉 (ViewModel) |
| ActivityComponent | @ActivityScoped | Activity 存活期間 |
| FragmentComponent | @FragmentScoped | Fragment 存活期間 |
| ViewComponent | @ViewScoped | 該 View 存活期間 |
透過 Hilt,我們能讓 Android 元件之間的相依性呈現清晰的層次結構,極大地提升了程式碼的可讀性與可測試性。