Android Hilt 教程 —— 一看就懂,一學就會
1. 什么是 Hilt?為什么要用 Hilt?
Hilt 是 Android 官方推薦的 依賴注入(DI)框架,基于 Dagger 開發,能夠大大簡化依賴注入的使用。
為什么要用 Hilt?
- 簡化依賴注入:不需要手寫復雜的 Dagger 代碼,Hilt 提供了簡單易懂的注解。
- 管理對象的生命周期:Hilt 會根據不同的組件(如 Activity、ViewModel、Application)自動管理依賴對象的創建和銷毀。
- 提高代碼的模塊化:通過 Hilt 提供的
@Module
,可以讓代碼更加清晰,方便維護和測試。
2. Hilt 的基本使用步驟
步驟 1:添加 Hilt 依賴
在 build.gradle (Project)
中添加 Hilt 插件:
buildscript {ext.hilt_version = '2.28-alpha'dependencies {classpath "com.google.dagger:hilt-android-gradle-plugin:$hilt_version"}
}新android studio 版本:
plugins {id 'com.google.dagger.hilt.android' version '2.51.1' apply false
}
在 app/build.gradle
中:
apply plugin: 'kotlin-kapt'
apply plugin: 'dagger.hilt.android.plugin'android {...
}dependencies {implementation("com.google.dagger:hilt-android:2.51.1")kapt("com.google.dagger:hilt-android-compiler:2.51.1")
}
注意: Hilt 需要 kotlin-kapt
來處理注解。
步驟 2:在 Application 級別啟用 Hilt
創建一個繼承 Application
的類,并添加 @HiltAndroidApp
注解。
import android.app.Application
import dagger.hilt.android.HiltAndroidApp@HiltAndroidApp
class MyApplication : Application()
作用: 讓 Hilt 進行全局依賴注入的初始化。
步驟 3:在 Activity 中使用 Hilt 進行依賴注入
在 MainActivity
中使用 @AndroidEntryPoint
讓 Hilt 自動提供對象:
import android.os.Bundle
import android.widget.TextView
import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatActivity
import dagger.hilt.android.AndroidEntryPoint
import javax.inject.Inject@AndroidEntryPoint
class MainActivity : AppCompatActivity() {@Injectlateinit var someClass: SomeClass // 直接注入對象private val viewModel: MainViewModel by viewModels()override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_main)// 顯示直接注入的結果val result1 = someClass.doSomething()findViewById<TextView>(R.id.tvDirectResult).text = "Direct injection: $result1"// 觀察 ViewModel 的結果viewModel.doWork { result ->findViewById<TextView>(R.id.tvViewModelResult).text = "ViewModel: $result"}}
}
步驟 4:在 ViewModel 中使用 Hilt 進行依賴注入
ViewModel 不能直接使用 @Inject
,需要 @HiltViewModel
注解:
import androidx.lifecycle.ViewModel
import dagger.hilt.android.lifecycle.HiltViewModel
import javax.inject.Inject@HiltViewModel
class MainViewModel @Inject constructor(private val someClass: SomeClass,private val someOtherClass: SomeOtherClass
) : ViewModel() {fun doWork(callback: (String) -> Unit) {val result1 = someClass.doSomething()val result2 = someOtherClass.doSomething()callback("$result1, $result2")}
}
步驟 5:創建普通類并讓 Hilt 提供實例
方法 1:直接使用 @Inject
注解(適用于簡單對象)
如果是 沒有構造參數 的類,可以直接用 @Inject
標注構造函數:
import javax.inject.Injectclass SomeClass @Inject constructor() {fun doSomething() = "Hello Hilt!"
}
Hilt 會自動創建 SomeClass
的實例,并在需要的地方注入。
方法 2:使用 @Module
提供實例(適用于需要配置的對象)
如果類 不能直接使用 @Inject
(例如:構造函數需要參數),需要在 @Module
里提供:
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import javax.inject.Singleton@Module
@InstallIn(SingletonComponent::class)
object AppModule {@Provides@Singletonfun provideSomeOtherClass(): SomeOtherClass {return SomeOtherClass()}
}
然后創建 SomeOtherClass
:
class SomeOtherClass {fun doSomething() = "Hello from SomeOtherClass!"
}
3. Hilt 的生命周期管理
Hilt 提供不同作用域,決定對象的生命周期:
@Singleton
—— 應用級,對象在整個應用生命周期內存在。@ActivityScoped
—— Activity 級,對象在同一個 Activity 內共享。@ViewModelScoped
—— ViewModel 級,對象綁定到ViewModel
的生命周期。@FragmentScoped
—— Fragment 級,對象在同一個 Fragment 內共享。
例如,如果 SomeOtherClass
只在 Activity
級別存在:
@Module
@InstallIn(ActivityComponent::class)
object ActivityModule {@Provides@ActivityScopedfun provideSomeOtherClass(): SomeOtherClass {return SomeOtherClass()}
}
4. 總結:為什么選擇 Hilt?
? 代碼簡潔:比 Dagger 需要更少的模板代碼。
? 易學易用:通過注解即可完成依賴注入。
? 自動管理生命周期:減少手動管理實例的工作。
? 官方支持:推薦用于 Android 開發。
按照本教程,已經學會如何在 Activity、ViewModel 和 普通類 中使用 Hilt 進行依賴注入
二 @Inject 后面為什么需要加上constructor()
在 Kotlin 中,@Inject constructor()
主要用于 依賴注入,告訴 Hilt 如何創建 SomeClass
的實例。
1. 為什么 @Inject
需要加 constructor()
?
在 Kotlin 里,constructor()
是 顯式聲明主構造函數 的方式,而 @Inject
只能標注 構造函數,不能直接標注類名。
例如:
class SomeClass @Inject constructor() { fun doSomething() = "Hello Hilt!"
}
這里 @Inject constructor()
表示:
- Hilt 需要通過這個構造函數來創建
SomeClass
的實例。 - Hilt 發現
@Inject
后,會自動提供SomeClass
的對象,無需手動創建。
2. 如果不加 constructor
會怎樣?
如果嘗試這樣寫:
class SomeClass @Inject { // ? 語法錯誤fun doSomething() = "Hello Hilt!"
}
會直接 編譯錯誤,因為 @Inject
必須作用于構造函數,而 constructor
是構造函數的 顯式聲明方式。
3. constructor
省略的情況
當類沒有顯式的 constructor
關鍵字時,它默認是 無參構造函數:
class SomeClass {fun doSomething() = "Hello!"
}
但如果要讓 Hilt 注入實例,必須 顯式 寫 @Inject constructor()
。
4. 帶參數的情況
如果 SomeClass
依賴其他類,比如 AnotherClass
,Hilt 也能自動提供:
class AnotherClass @Inject constructor()class SomeClass @Inject constructor(private val anotherClass: AnotherClass) {fun doSomething() = "Using ${anotherClass.toString()}"
}
Hilt 會先創建 AnotherClass
,然后再創建 SomeClass
并把 AnotherClass
傳進去。
總結
? @Inject constructor()
是 Hilt 依賴注入的核心,告訴 Hilt 如何實例化一個類。
? 不能直接寫 @Inject
,必須加 constructor()
。
? 如果類有依賴,Hilt 會自動提供并注入參數。
這樣,Hilt 就能幫們自動管理 SomeClass
的創建,而不需要手動 new SomeClass()
了!
三 代碼含義
1
// 觀察 ViewModel 的結果
viewModel.doWork { result ->
findViewById<TextView>(R.id.tvViewModelResult).text = "ViewModel: $result"
}2
fun doWork(callback: (String) -> Unit) {
val result1 = someClass.doSomething()
val result2 = someOtherClass.doSomething()
callback("$result1, $result2")
}
這段代碼涉及 Lambda 回調 機制,它的作用是異步或延遲地獲取 doWork
方法執行后的結果,并在外部(比如 UI 層)進行處理。
代碼解析
1. 觀察 ViewModel 的結果
viewModel.doWork { result ->findViewById<TextView>(R.id.tvViewModelResult).text = "ViewModel: $result"
}
- 這里
viewModel.doWork
是一個方法,它接收一個 Lambda 回調,即{ result -> ... }
。 result
是 doWork 方法執行完成后,回傳的結果。findViewById<TextView>(R.id.tvViewModelResult).text = "ViewModel: $result"
這行代碼的意思是:當doWork
任務完成后,將result
賦值給TextView
,在 UI 界面顯示出來。
2. doWork
方法定義
fun doWork(callback: (String) -> Unit) {val result1 = someClass.doSomething()val result2 = someOtherClass.doSomething()callback("$result1, $result2")
}
doWork
方法接收一個參數callback
,類型是(String) -> Unit
,即 一個以String
為參數、無返回值的 Lambda 表達式。someClass.doSomething()
和someOtherClass.doSomething()
是執行的兩個操作,它們可能返回字符串類型的結果。callback("$result1, $result2")
:- 這個
callback
就是上面調用doWork
時傳入的{ result -> ... }
Lambda。 "$result1, $result2"
生成一個字符串,格式類似"value1, value2"
。- 這個字符串作為參數,傳遞給
callback
,從而調用{ result -> ... }
代碼塊,并將"$result1, $result2"
賦值給result
。
- 這個
result
是什么?
result
是doWork
任務執行后,回傳給 Lambda 代碼塊的結果。- 具體值取決于
someClass.doSomething()
和someOtherClass.doSomething()
的返回值。
callback("$result1, $result2")
是什么?
callback("$result1, $result2")
的作用是:- 生成一個字符串
"$result1, $result2"
(比如"Hello, World"
)。 - 調用外部傳入的 Lambda 代碼塊,并把這個字符串作為參數傳遞出去。
- 生成一個字符串
執行流程示例
假設:
class SomeClass {fun doSomething(): String = "Hello"
}class SomeOtherClass {fun doSomething(): String = "World"
}val someClass = SomeClass()
val someOtherClass = SomeOtherClass()
那么 doWork
執行后:
doWork { result -> println("回調結果: $result") }
會輸出:
回調結果: Hello, World
總結
doWork
方法執行后,someClass.doSomething()
和someOtherClass.doSomething()
生成兩個字符串。callback("$result1, $result2")
將它們拼接,并調用外部 Lambda 代碼塊,把結果傳出去。result
是 Lambda 代碼塊接收的參數,即"Hello, World"
這樣的字符串。- 在
viewModel.doWork
調用時,最終TextView
顯示"ViewModel: Hello, World"
。
這種方式適用于異步任務或解耦邏輯的情況,比如 網絡請求、數據庫操作、后臺計算等,執行完成后用回調通知 UI 層更新數據。
四 手動 clean + build
Build → Clean Project
Build → Rebuild Project
五 Android Hilt 入門教程(補充)
Hilt 是 Android 官方推薦的 依賴注入框架,它基于 Dagger,簡化了依賴管理,適用于 MVVM 架構,提高了代碼的可維護性。
1?? 為什么要用 Hilt?
在 Android 開發中,們通常需要手動創建和管理對象,例如 ViewModel
、Repository
、Retrofit
等。Hilt 可以自動管理這些對象的創建和生命周期,讓們專注于業務邏輯,而不是手動實例化對象。
? Hilt 的優點:
?? 自動管理依賴,避免手動創建實例
?? ViewModel 支持,與 Jetpack 組件無縫集成
?? 作用域管理,不同組件(Activity、Fragment)能獲得合適的對象
?? 簡化 Dagger 依賴注入,代碼更簡潔
2?? Hilt 的基本使用
📌(1)添加 Hilt 依賴
與第一點相同
plugins {id("com.android.application")id("kotlin-android")id("kotlin-kapt")id("com.google.dagger.hilt.android")
}android {namespace 'com.test.hiltstudy'compileSdk 35defaultConfig {applicationId "com.test.hiltstudy"minSdk 24targetSdk 35versionCode 1versionName "1.0"testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"}buildTypes {release {minifyEnabled falseproguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'}}compileOptions {sourceCompatibility JavaVersion.VERSION_1_8targetCompatibility JavaVersion.VERSION_1_8}kotlinOptions {jvmTarget = '1.8'}
}Allow references to generated code
//kapt {
// correctErrorTypes true
//}dependencies {implementation libs.androidx.core.ktximplementation libs.androidx.appcompatimplementation libs.materialimplementation libs.androidx.activityimplementation libs.androidx.constraintlayouttestImplementation libs.junitandroidTestImplementation libs.androidx.junitandroidTestImplementation libs.androidx.espresso.core// Hilt Dependenciesimplementation("com.google.dagger:hilt-android:2.51.1")kapt("com.google.dagger:hilt-android-compiler:2.51.1")// Fragment KTX for viewModels() delegateimplementation("androidx.fragment:fragment-ktx:1.6.2")// ViewModel
// implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.2")
// implementation("androidx.activity:activity-ktx:1.8.1")
//retrofitimplementation("com.squareup.retrofit2:converter-gson:2.9.0")}
📌(2)初始化 Hilt
在 AndroidManifest.xml
:
<applicationandroid:name=".MyApplication"...>
</application>
然后創建 MyApplication.kt
:
@HiltAndroidApp
class MyApplication : Application()
🔹 @HiltAndroidApp
用于初始化 Hilt,它會在 App 啟動時配置依賴注入。
📌(3)在 Activity/Fragment 使用 Hilt
在 Activity 里啟用 Hilt
@AndroidEntryPoint
class MainActivity : AppCompatActivity() {@Injectlateinit var someClass: SomeClass // 自動注入
}
🔹 @AndroidEntryPoint
標記 Activity 以支持 Hilt
🔹 @Inject lateinit var someClass: SomeClass
直接注入對象
在 Fragment 里啟用 Hilt
@AndroidEntryPoint
class MainFragment : Fragment() {@Injectlateinit var someRepository: SomeRepository
}
💡 Activity 和 Fragment 都必須加 @AndroidEntryPoint
才能使用 Hilt 注入的對象!
📌(4)在 ViewModel 里使用 Hilt
@HiltViewModel
class MainViewModel @Inject constructor(private val repository: SomeRepository
) : ViewModel() {fun fetchData() = repository.getData()
}
在 Activity
或 Fragment
里:
private val viewModel: MainViewModel by viewModels()
🔹 Hilt 自動創建 MainViewModel
,不用 ViewModelProvider
手動實例化。
📌(5)創建 Hilt 模塊(Module)
如果 SomeRepository
不能用 @Inject
直接構造,比如 Retrofit,們需要 使用 Module 提供實例:
@Module
@InstallIn(SingletonComponent::class) // 作用于整個應用生命周期
object AppModule {@Provides@Singletonfun provideRetrofit(): Retrofit {return Retrofit.Builder().baseUrl("https://api.example.com/").addConverterFactory(GsonConverterFactory.create()).build()}@Provides@Singletonfun provideApiService(retrofit: Retrofit): ApiService {return retrofit.create(ApiService::class.java)}
}
🔹 @Module
標記為 Hilt 模塊
🔹 @Provides
提供依賴
🔹 @Singleton
表示單例
3?? Hilt 作用域
作用域 | 說明 | 示例 |
---|---|---|
@Singleton | 全局單例,應用級共享 | Retrofit、數據庫 |
@ActivityScoped | 只在 Activity 里共享 | 共享 ViewModel |
@ViewModelScoped | 只在 ViewModel 里共享 | Repository |
4?? Hilt 實戰示例
1?? 創建一個 Repository
class SomeRepository @Inject constructor() {fun getData(): String = "Hello from Repository"
}
2?? 在 ViewModel 里注入
@HiltViewModel
class MainViewModel @Inject constructor(private val repository: SomeRepository
) : ViewModel() {fun fetchData(): String = repository.getData()
}
3?? 在 Activity 里獲取數據
@AndroidEntryPoint
class MainActivity : AppCompatActivity() {private val viewModel: MainViewModel by viewModels()override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_main)findViewById<TextView>(R.id.textView).text = viewModel.fetchData()}
}
? 運行后,TextView 顯示 "Hello from Repository"
🎉
5?? 總結
🔹 @HiltAndroidApp
讓應用支持 Hilt
🔹 @AndroidEntryPoint
用于 Activity/Fragment
🔹 @HiltViewModel
用于 ViewModel
🔹 @Inject
直接注入類實例
🔹 @Module + @Provides
提供無法直接注入的對象(如 Retrofit)
🔹 @Singleton
、@ActivityScoped
控制對象生命周期
Hilt 讓 依賴注入變得簡單高效,可以自動管理對象,提升代碼的可維護性。
五 為什么MainActivity
等類都必須寫Hilt注解
- 當在
MainActivity
中使用by viewModels()
時,Android 系統需要創建MainViewModel
的實例 MainViewModel
的構造函數需要一個SomeRepository
參數- 由于
SomeRepository
使用了@Inject
注解,它只能通過 Hilt 的依賴注入系統來創建和管理
這就造成了一個依賴鏈:
MainActivity
需要MainViewModel
MainViewModel
需要SomeRepository
SomeRepository
由 Hilt 管理
所以當使用了 Hilt 來管理某個依賴(如 SomeRepository
)時,所有需要使用這個依賴的類(如 MainViewModel
)也必須通過 Hilt 來管理。這不是"強行綁定",而是依賴注入系統工作的必然要求。
如果不想使用 Hilt,需要:
- 移除
SomeRepository
的@Inject
注解 - 手動創建
SomeRepository
和MainViewModel
的實例 - 實現自定義的
ViewModelFactory
但這樣會失去依賴注入帶來的好處,如:
- 依賴的自動管理
- 生命周期的自動處理
- 測試時依賴的容易替換
- 代碼的解耦
六 Hilt 管理對象的原理
Hilt 是基于 Dagger 的 依賴注入(Dependency Injection, DI)框架。它在編譯期生成代碼,自動管理對象的創建、注入和生命周期。
1?? 自動管理依賴(對象創建)原理
當寫:
class MyRepository @Inject constructor(private val apiService: ApiService
)
Hilt 會在 編譯期生成一段 Dagger 代碼,負責:
- 創建
MyRepository
- 自動找到
ApiService
的實例(如果也可以被@Inject
或@Provides
)
👉 總結:
Hilt 使用 @Inject
和 @Module + @Provides
來定義 對象之間的依賴關系圖,并在編譯時生成創建這些對象的代碼。
2?? 生命周期的自動處理
Hilt 把依賴對象和 Android 組件的生命周期綁定在一起,通過作用域注解(Scope)來完成。
🧩 作用域示例:
注解 | 生命周期 | 示例 |
---|---|---|
@Singleton | 應用級別(Application 生命周期) | Retrofit、Room |
@ActivityScoped | 綁定到某個 Activity 生命周期 | 當前 Activity 的共享依賴 |
@ViewModelScoped | ViewModel 生命周期 | 當前 ViewModel 獨享的對象 |
? 原理:
Hilt 在每個作用域下生成一個 Dagger 組件(Component):
SingletonComponent
對應 ApplicationActivityComponent
對應 ActivityViewModelComponent
對應 ViewModel- …
這些組件管理它們生命周期內的對象,只要組件存在,對象就一直存活;組件銷毀,對象就自動釋放。
3?? 測試時依賴容易替換(可插拔)
Hilt 支持測試環境下 替換真實依賴為 Mock 或 Fake,這是 DI 的巨大優勢。
? 替換方式:
@HiltAndroidTest
@UninstallModules(AppModule::class) // 卸載正式模塊
class MyTest {@Module@InstallIn(SingletonComponent::class)object TestModule {@Providesfun provideFakeApi(): ApiService = FakeApiService()}
}
? 測試時,Hilt 用 TestModule
替換 AppModule
,讓測試邏輯而不是網絡。
4?? 解耦代碼的核心原理
? 傳統寫法(耦合):
val repo = MyRepository(ApiService())
MyRepository
硬編碼依賴了ApiService
,不利于替換、擴展、測試。
? Hilt 寫法(解耦):
class MyRepository @Inject constructor(private val api: ApiService)
MyRepository
只依賴 抽象接口ApiService
是由外部(Hilt)提供,未來替換為FakeApiService
不用改業務邏輯- 解耦 = 高擴展性 + 高可測試性
🔧 總結原理圖(類比管道工廠)
Hilt 功能 | 原理 | 類比 |
---|---|---|
自動注入 | 編譯期生成依賴圖 | 自動搭建水管連接 |
生命周期管理 | 每個作用域有專屬組件管理對象 | 活水池(組件)存在水就流動(對象存活) |
測試替換 | 支持模塊替換 | 換水源(Fake)測試流速(邏輯) |
解耦結構 | 依賴抽象、注入實現 | 插拔模塊化水管,便于維護 |
🚀 總結一句話:
Hilt = 編譯期生成對象工廠 + 生命周期管家 + 解耦利器 + 測試友好助手,讓寫少但可維護性更高的代碼。
六 Hilt 依賴注入結構圖解
🧠 Hilt 架構核心
+---------------------------+| Application || (HiltApplication class) || @HiltAndroidApp |+---------------------------+|v+---------------------------+| SingletonComponent || @InstallIn(Singleton...) || -> Retrofit, Room, Repo |+---------------------------+|v+---------------------------+| ActivityComponent || @InstallIn(Activity...) || -> Activity 作用域對象 |+---------------------------+|v+---------------------------+| ViewModelComponent || @HiltViewModel || -> ViewModel 的依賴 |+---------------------------+|v+---------------------------+| FragmentComponent || @AndroidEntryPoint || -> Fragment 注入依賴 |+---------------------------+
當然可以!下面是 **Hilt 的內部結構原理圖解** 的 `.md`(Markdown)格式說明,適合用于技術文檔、GitHub README 或團隊協作文檔中:---?```md
# 🛠? Hilt 依賴注入結構圖解## 🧠 Hilt 架構核心?```plaintext+---------------------------+| Application || (HiltApplication class) || @HiltAndroidApp |+---------------------------+|v+---------------------------+| SingletonComponent || @InstallIn(Singleton...) || -> Retrofit, Room, Repo |+---------------------------+|v+---------------------------+| ActivityComponent || @InstallIn(Activity...) || -> Activity 作用域對象 |+---------------------------+|v+---------------------------+| ViewModelComponent || @HiltViewModel || -> ViewModel 的依賴 |+---------------------------+|v+---------------------------+| FragmentComponent || @AndroidEntryPoint || -> Fragment 注入依賴 |+---------------------------+
🧩 作用域綁定關系
組件類型 | 對應生命周期 | 示例依賴項 |
---|---|---|
SingletonComponent | Application 全局單例 | Retrofit、數據庫等 |
ActivityComponent | 每個 Activity 獨立 | 當前 Activity 的共享依賴 |
ViewModelComponent | 每個 ViewModel 獨立 | 倉庫、業務類 |
FragmentComponent | 每個 Fragment 獨立 | 當前 Fragment 的依賴 |
🔄 流程示意(依賴注入過程)
1. App 啟動時,Hilt 生成 SingletonComponent
2. Activity 啟動時,注入 ActivityComponent 作用域依賴
3. Fragment 加載時,注入 FragmentComponent 作用域依賴
4. ViewModel 被創建時,注入 ViewModelComponent 中依賴
5. 每個 Component 都可以從其上層 Component 獲取依賴
? 示例:自動注入過程
// 注入 ViewModel
@AndroidEntryPoint
class MainActivity : AppCompatActivity() {private val viewModel: MainViewModel by viewModels()
}// 提供依賴
@HiltViewModel
class MainViewModel @Inject constructor(private val repo: SomeRepository
) : ViewModel()// 倉庫依賴提供
class SomeRepository @Inject constructor(private val api: ApiService
)
🧪 測試支持
@HiltAndroidTest
@UninstallModules(AppModule::class)
class MyTest {@Module@InstallIn(SingletonComponent::class)object TestModule {@Providesfun provideFakeApi(): ApiService = FakeApiService()}
}
八 Hilt為什么是解耦利器 和測試友好助手
Hilt 之所以被稱為 “解耦利器” 和 “測試友好助手”,是因為它能讓 類之間的依賴關系更加松散,并且 支持在測試時輕松替換依賴。
我們從 解耦(Decoupling) 和 測試友好(Testability) 兩個方面分別解釋,并提供示例代碼來說明。
🎯 1. Hilt 是如何解耦代碼的?
? 傳統寫法(緊耦合,難以擴展)
class MyRepository {private val apiService = ApiService() // 直接創建實例(強依賴)fun fetchData(): String {return apiService.getData()}
}
? 問題
MyRepository
直接依賴ApiService
,導致:- 難以替換(如果要改用
FakeApiService
進行測試,就得修改MyRepository
代碼) - 擴展性差(如果
ApiService
需要不同的實現方式,就得改MyRepository
) - 不適合單元測試(不能注入模擬數據)
- 難以替換(如果要改用
? 使用 Hilt 進行解耦
class MyRepository @Inject constructor(private val apiService: ApiService) {fun fetchData(): String {return apiService.getData()}
}
Hilt 提供的 ApiService
實例
@Module
@InstallIn(SingletonComponent::class)
object AppModule {@Provides@Singletonfun provideApiService(): ApiService {return RealApiService()}
}
? 解耦優勢
- MyRepository 只依賴
ApiService
抽象,不關心ApiService
具體是怎么來的 - 可以輕松替換
ApiService
(比如切換到FakeApiService
進行測試) - 代碼更清晰,職責更明確(
Hilt
負責管理依賴,不再手動new
)
🔬 2. Hilt 是如何讓測試變得更簡單的?
Hilt 允許我們在測試時 替換真實依賴,避免復雜的網絡請求、數據庫操作等,從而更快、更穩定地測試業務邏輯。
? 1. 不使用 Hilt,測試困難
@Test
fun testFetchData() {val repo = MyRepository(ApiService()) // 依賴真實的 ApiServiceval result = repo.fetchData()assertEquals("Expected Data", result) // 可能失敗,因為是真實數據
}
? 問題
MyRepository
無法使用 Mock 依賴- 每次測試都會訪問真實 API(影響速度、可能失敗)
- 代碼可測試性 非常低
? 2. 使用 Hilt 輕松替換依賴
🛠? 在測試中提供 FakeApiService
@HiltAndroidTest
@UninstallModules(AppModule::class) // 先卸載正式模塊
class MyRepositoryTest {@Module@InstallIn(SingletonComponent::class)object TestModule {@Providesfun provideFakeApiService(): ApiService {return object : ApiService {override fun getData(): String {return "Fake Data"}}}}@Injectlateinit var repository: MyRepository@get:Rulevar hiltRule = HiltAndroidRule(this)@Beforefun setup() {hiltRule.inject() // 讓 Hilt 注入測試依賴}@Testfun testFetchData() {val result = repository.fetchData()assertEquals("Fake Data", result) // 100% 可預測的測試結果}
}
? 測試優勢
- 自動替換真實依賴(不再訪問網絡或數據庫)
- 測試速度更快(不依賴外部服務)
- Mock 數據可預測(不會受外部 API 變動影響)
📌 總結
特點 | 傳統依賴方式 | 使用 Hilt |
---|---|---|
代碼解耦 | 直接 new 對象,強依賴具體實現 | 依賴抽象,Hilt 負責提供實現 |
可擴展性 | 變更時需要修改多個類 | 只需修改 @Module 提供的依賴 |
測試友好 | 依賴真實 API,難以 Mock | 輕松替換 Mock 依賴,提高測試效率 |
代碼可維護性 | 依賴關系混亂,難以管理 | 依賴關系清晰,代碼模塊化 |
🚀 一句話總結
Hilt = 解耦利器 + 測試友好助手,讓的代碼 更模塊化、更易測試、更易維護!
九 Hilt為什么是對象工廠 和 生命周期管家 ?
Hilt 之所以是 對象工廠 和 生命周期管家,是因為它能夠 自動創建并管理依賴對象,并且可以 自動適配依賴對象的生命周期,避免手動管理帶來的復雜性和潛在的內存泄漏。
🎭 1. Hilt 是對象工廠(自動創建并管理依賴對象)
在沒有 Hilt 的情況下,我們通常需要手動創建對象:
class MyRepository {private val apiService = ApiService() // 直接創建實例
}
? 問題
MyRepository
依賴ApiService
,必須手動new
,不靈活- 如果
ApiService
還依賴Retrofit
,就需要new
多個對象,依賴鏈復雜
? Hilt 作為對象工廠
Hilt 通過 @Module
+ @Provides
或 @Inject
構造注入 自動創建對象:
class MyRepository @Inject constructor(private val apiService: ApiService) { }
Hilt 自動提供 ApiService
實例
@Module
@InstallIn(SingletonComponent::class)
object AppModule {@Provides@Singletonfun provideApiService(): ApiService {return Retrofit.Builder().baseUrl("https://api.example.com/").addConverterFactory(GsonConverterFactory.create()).build().create(ApiService::class.java)}
}
🎯 關鍵點
- Hilt 自動創建
ApiService
,并將其注入到MyRepository
- 我們不需要手動
new
,Hilt 充當工廠,自動提供對象 - 如果
ApiService
還有依賴(如Retrofit
),Hilt 也會自動解析并注入
🕰? 2. Hilt 是生命周期管家(自動管理對象生命周期)
在 Android 開發中,不同作用域的對象需要不同的生命周期,比如:
- Application 級別的單例(整個應用共享)
- Activity 級別的實例(Activity 銷毀時自動清理)
- Fragment 級別的實例(Fragment 關閉時釋放)
? 傳統方式:手動管理生命周期
class MainActivity : AppCompatActivity() {private val repository = MyRepository(ApiService()) // 手動創建,難以管理
}
? 問題
- 全局變量會導致內存泄漏
- Activity 重建(如旋轉屏幕)后,數據可能丟失
- 手動管理生命周期非常繁瑣
? Hilt 自動管理生命周期
Hilt 通過作用域(@InstallIn(Component::class)
)自動匹配生命周期:
🎯 Application 作用域(全局單例)
@InstallIn(SingletonComponent::class) // Application 級別
@Module
object AppModule {@Provides@Singletonfun provideMyRepository(apiService: ApiService): MyRepository {return MyRepository(apiService)}
}
- 全局單例:
SingletonComponent
作用域下的對象,整個應用生命周期內存儲 - 避免重復創建:所有使用
MyRepository
的地方,都共享同一個實例
🎯 Activity 作用域(Activity 級別)
@AndroidEntryPoint
class MainActivity : AppCompatActivity() {private val viewModel: MainViewModel by viewModels() // Hilt 自動管理生命周期
}
@HiltViewModel
class MainViewModel @Inject constructor(private val repository: MyRepository
) : ViewModel()
@HiltViewModel
綁定 ViewModel 生命周期,當Activity
關閉時,ViewModel 也會自動銷毀MyRepository
仍然是Singleton
作用域的,所以MainViewModel
依賴它,但不會重復創建
🎯 Fragment 作用域
如果 Fragment
需要自己的 ViewModel
:
@AndroidEntryPoint
class MyFragment : Fragment() {private val viewModel: MyViewModel by viewModels()
}
@HiltViewModel
class MyViewModel @Inject constructor(private val repository: MyRepository
) : ViewModel()
MyViewModel
與Fragment
綁定,Fragment 銷毀時自動釋放- 不會因為
Activity
變化導致數據丟失
📌 結論
特性 | 傳統方式 | 使用 Hilt |
---|---|---|
對象管理 | 手動 new ,難以管理 | Hilt 自動創建并管理依賴 |
依賴關系 | 需要手動傳遞依賴 | Hilt 通過 @Inject 自動注入 |
生命周期管理 | 需要手動釋放對象,避免內存泄漏 | Hilt 自動匹配對象生命周期 |
測試支持 | 需要大量 Mock | Hilt 允許輕松替換依賴 |
💡 總結
Hilt 作為 “對象工廠”,自動創建并管理依賴對象
Hilt 作為 “生命周期管家”,自動管理作用域,防止內存泄漏
這樣,我們就能更專注于業務邏輯,而不用操心依賴創建和生命周期管理!
十 @Inject注解
@Inject
是 Hilt 依賴注入的核心注解,它的作用是 讓 Hilt 知道如何創建和提供一個類的實例,從而 自動管理對象的依賴關系。提到的兩種使用方式(構造函數注入 和 字段注入)確實是 @Inject
的兩個關鍵用途,下面詳細解釋。
📌 1. @Inject
作用之一:構造函數注入
構造函數注入(Constructor Injection)用于 告訴 Hilt 如何創建一個類的實例。當一個類的構造函數上加上 @Inject
,Hilt 就會自動知道如何實例化它。
? 示例
class SomeClass @Inject constructor() {fun doSomething() = "Hello Hilt!"
}
@Inject constructor()
告訴 Hilt 這個類可以被自動創建,無需手動new SomeClass()
- 任何需要
SomeClass
的地方,Hilt 都可以自動提供它 - 適用于 無狀態類(即不需要復雜的初始化)
🚀 使用 @Inject
的優勢
? 無需手動 new 對象,Hilt 負責實例化
? 減少樣板代碼,避免工廠模式(Factory)或手寫依賴注入
? 保證依賴一致性,不會意外創建多個 SomeClass
實例
📌 2. @Inject
作用之二:字段注入
字段注入(Field Injection)用于 在類內部自動注入依賴對象,通常用于 Activity
、Fragment
或 ViewModel
,因為它們的實例是由 Android 框架創建的,不能使用構造函數注入。
? 示例
@AndroidEntryPoint
class MainActivity : AppCompatActivity() {@Injectlateinit var someClass: SomeClass // 自動注入override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_main)// 使用被 Hilt 自動注入的對象Log.d("HiltExample", someClass.doSomething()) // 輸出 "Hello Hilt!"}
}
📌 關鍵點
@Inject lateinit var someClass: SomeClass
- 讓 Hilt 自動創建
SomeClass
實例,并注入到someClass
變量中 - 不需要
new SomeClass()
- 讓 Hilt 自動創建
@AndroidEntryPoint
必須加在Activity
或Fragment
上,否則 Hilt 無法注入- 這是因為
Activity
和Fragment
由 Android 框架管理,Hilt 需要特殊處理它們的依賴注入
- 這是因為
🚀 使用 @Inject
的優勢
? Hilt 自動創建并注入實例,無需手動初始化
? 避免 lateinit
為空的問題,Hilt 負責對象生命周期
? 簡化依賴管理,代碼更清晰、可維護性更強
📌 3. @Inject
與 Hilt 作用域
不同的 @Inject
依賴可以擁有不同的生命周期
@Singleton
class SomeSingletonClass @Inject constructor()
- 這個
SomeSingletonClass
只會被創建一次,整個應用生命周期都能共享 - Hilt 自動管理它的生命周期,不會意外地創建多個實例
不同作用域示例:
@ActivityScoped
class SomeActivityScopedClass @Inject constructor()
ActivityScoped
:每個 Activity 會有自己的實例SingletonComponent
:全局單例ViewModelScoped
:與 ViewModel 生命周期一致
📌 4. @Inject
不能用于接口,需要 @Module
提供
如果 @Inject
用在 接口 上,會報錯:
interface ApiService {fun fetchData(): String
}
? 直接 @Inject
不行:
class ApiServiceImpl @Inject constructor() : ApiService {override fun fetchData() = "API Data"
}
👉 必須使用 @Module
提供接口實例
@Module
@InstallIn(SingletonComponent::class)
object AppModule {@Providesfun provideApiService(): ApiService {return ApiServiceImpl()}
}
📌 關鍵點
- 如果是普通類,
@Inject constructor()
就夠了 - 如果是接口,必須用
@Module
和@Provides
提供
🎯 結論
方式 | 使用場景 | 作用 |
---|---|---|
@Inject constructor() | 普通類 | 讓 Hilt 知道如何創建這個類 |
@Inject lateinit var | Activity、Fragment、ViewModel | 讓 Hilt 自動注入對象實例 |
@Module + @Provides | 接口或第三方庫 | 讓 Hilt 提供無法直接 @Inject 的對象 |
🚀 一句話總結
@Inject
讓 Hilt 知道如何創建對象,而@Inject lateinit var
讓 Hilt 自動注入對象!
十一 Hilt其他注解
在 Android Hilt 中,除了 @Inject
之外,還有多個核心注解,它們負責不同的依賴注入功能,包括作用域管理、模塊提供依賴、綁定接口、生命周期管理等。以下是 Hilt 的核心注解及其詳細解析,并附上代碼示例。
📌 1. @HiltAndroidApp
作用:
- 標記
Application
類,讓 Hilt 生成依賴注入的代碼 - 必須 在
Application
類上使用
? 示例
@HiltAndroidApp
class MyApplication : Application()
🚀 關鍵點
? Hilt 會在應用啟動時初始化依賴注入
? 生成 Hilt_MyApplication
代碼,Hilt 依賴的入口
? 必須加,否則 Hilt 無法工作
📌 2. @AndroidEntryPoint
作用:
- 用于
Activity
、Fragment
、Service
等,讓它們支持 Hilt 依賴注入 - 必須加,否則 Hilt 無法注入對象
? 示例
@AndroidEntryPoint
class MainActivity : AppCompatActivity() {@Injectlateinit var someClass: SomeClass // 自動注入
}
🚀 關鍵點
? Activity
、Fragment
、Service
必須加,否則 @Inject
不會生效
? Hilt 會自動在內部生成依賴注入代碼
? Fragment 依賴的 Activity 也必須有 @AndroidEntryPoint
,否則會崩潰
📌 3. @Inject
作用:
- 用于 構造函數 或 字段,讓 Hilt 知道如何創建和注入對象
? 示例
構造函數注入
class SomeClass @Inject constructor() {fun doSomething() = "Hello Hilt!"
}
字段注入
@AndroidEntryPoint
class MainActivity : AppCompatActivity() {@Injectlateinit var someClass: SomeClass
}
🚀 關鍵點
? 構造函數上加 @Inject
,Hilt 知道如何創建對象
? 字段加 @Inject
,Hilt 會自動提供依賴
📌 4. @Module
+ @InstallIn
作用:
- 提供無法直接
@Inject
的對象(如接口、第三方庫) - 定義依賴的作用域
? 示例
@Module
@InstallIn(SingletonComponent::class) // 全局單例
object AppModule {@Provides@Singletonfun provideApiService(): ApiService {return Retrofit.Builder().baseUrl("https://api.example.com/").addConverterFactory(GsonConverterFactory.create()).build().create(ApiService::class.java)}
}
🚀 關鍵點
? @Module
讓 Hilt 知道這里提供依賴
? @InstallIn
確定依賴的作用域(SingletonComponent
表示全局單例)
📌 5. @Provides
作用:
- 提供對象實例(適用于無法
@Inject
的情況,如第三方庫)
? 示例
@Module
@InstallIn(SingletonComponent::class)
object AppModule {@Providesfun provideSomeClass(): SomeClass {return SomeClass()}
}
🚀 關鍵點
? 如果 SomeClass
不能 @Inject
,就用 @Provides
提供
? 返回類型就是 Hilt 提供的類型
📌 6. @Binds
作用:
- 用于接口的實現綁定
- 比
@Provides
更高效(少了一次方法調用)
? 示例
interface ApiService {fun fetchData(): String
}class ApiServiceImpl @Inject constructor() : ApiService {override fun fetchData() = "API Data"
}@Module
@InstallIn(SingletonComponent::class)
abstract class AppModule {@Bindsabstract fun bindApiService(impl: ApiServiceImpl): ApiService
}
🚀 關鍵點
? @Binds
只能用于 abstract
方法,不能有邏輯
? 比 @Provides
更高效,但 @Provides
更靈活
📌 7. 作用域注解
作用:
- 控制 對象的生命周期,防止重復創建
- 適用于
@Provides
或@Inject
提供的對象
? 示例
@Singleton
class SomeSingletonClass @Inject constructor()
作用域 | 適用范圍 | 生命周期 |
---|---|---|
SingletonComponent | 全局 | 應用生命周期 |
ActivityRetainedComponent | ViewModel | Activity 重新創建時依然存在 |
ActivityComponent | Activity | Activity 銷毀時釋放 |
FragmentComponent | Fragment | Fragment 銷毀時釋放 |
📌 8. @HiltViewModel
作用:
- 讓 ViewModel 支持 Hilt 注入
- 簡化 ViewModel 創建
? 示例
@HiltViewModel
class MainViewModel @Inject constructor(private val repository: MyRepository
) : ViewModel()
@AndroidEntryPoint
class MainActivity : AppCompatActivity() {private val viewModel: MainViewModel by viewModels()
}
🚀 關鍵點
? @HiltViewModel
必須配合 @AndroidEntryPoint
的 Activity
或 Fragment
? ViewModel 不用手動創建,Hilt 自動管理
📌 9. @EntryPoint
作用:
- 用于無法使用
@AndroidEntryPoint
的類 - 如
ContentProvider
、BroadcastReceiver
? 示例
@EntryPoint
@InstallIn(SingletonComponent::class)
interface MyEntryPoint {fun getSomeClass(): SomeClass
}// 使用 EntryPoint
val someClass = EntryPointAccessors.fromApplication(context, MyEntryPoint::class.java).getSomeClass()
🚀 關鍵點
? 適用于無法直接 @Inject
的情況
? Hilt 依然能提供依賴
🎯 結論
注解 | 作用 |
---|---|
@HiltAndroidApp | 讓 Hilt 初始化依賴注入(必須加在 Application ) |
@AndroidEntryPoint | 讓 Activity 、Fragment 、Service 支持 Hilt |
@Inject | 構造函數注入 & 字段注入 |
@Module + @InstallIn | 提供無法 @Inject 的依賴 |
@Provides | 直接提供實例(如第三方庫) |
@Binds | 綁定接口實現(比 @Provides 高效) |
@Singleton | 作用域管理(全局單例) |
@HiltViewModel | Hilt ViewModel 支持 |
@EntryPoint | 用于 BroadcastReceiver 、ContentProvider |
🚀 總結:Hilt 通過 @Inject
、@Module
、@Provides
等注解,讓 對象創建 & 生命周期管理 自動化,大幅減少樣板代碼,提高可維護性!
參考
google Hilt 教程