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 插件:
gradle
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
中:
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
注解。
kotlin
import android.app.Application
import dagger.hilt.android.HiltAndroidApp
@HiltAndroidApp
class MyApplication : Application()
作用: 让 Hilt 进行全局依赖注入的初始化。
步骤 3:在 Activity 中使用 Hilt 进行依赖注入
在 MainActivity
中使用 @AndroidEntryPoint
让 Hilt 自动提供对象:
kotlin
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() {
@Inject
lateinit 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
注解:
kotlin
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
标注构造函数:
kotlin
import javax.inject.Inject
class SomeClass @Inject constructor() {
fun doSomething() = "Hello Hilt!"
}
Hilt 会自动创建 SomeClass
的实例,并在需要的地方注入。
方法 2:使用 @Module
提供实例(适用于需要配置的对象)
如果类 不能直接使用 @Inject
(例如:构造函数需要参数),需要在 @Module
里提供:
kotlin
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
@Singleton
fun provideSomeOtherClass(): SomeOtherClass {
return SomeOtherClass()
}
}
然后创建 SomeOtherClass
:
kotlin
class SomeOtherClass {
fun doSomething() = "Hello from SomeOtherClass!"
}
3. Hilt 的生命周期管理
Hilt 提供不同作用域,决定对象的生命周期:
@Singleton
------ 应用级,对象在整个应用生命周期内存在。@ActivityScoped
------ Activity 级,对象在同一个 Activity 内共享。@ViewModelScoped
------ ViewModel 级 ,对象绑定到ViewModel
的生命周期。@FragmentScoped
------ Fragment 级,对象在同一个 Fragment 内共享。
例如,如果 SomeOtherClass
只在 Activity
级别存在:
kotlin
@Module
@InstallIn(ActivityComponent::class)
object ActivityModule {
@Provides
@ActivityScoped
fun provideSomeOtherClass(): SomeOtherClass {
return SomeOtherClass()
}
}
4. 总结:为什么选择 Hilt?
✅ 代码简洁 :比 Dagger 需要更少的模板代码。
✅ 易学易用 :通过注解即可完成依赖注入。
✅ 自动管理生命周期 :减少手动管理实例的工作。
✅ 官方支持:推荐用于 Android 开发。
按照本教程,已经学会如何在 Activity 、ViewModel 和 普通类 中使用 Hilt 进行依赖注入
二 @Inject 后面为什么需要加上constructor()
在 Kotlin 中,@Inject constructor()
主要用于 依赖注入 ,告诉 Hilt 如何创建 SomeClass
的实例。
1. 为什么 @Inject
需要加 constructor()
?
在 Kotlin 里,constructor()
是 显式声明主构造函数 的方式,而 @Inject
只能标注 构造函数,不能直接标注类名。
例如:
kotlin
class SomeClass @Inject constructor() {
fun doSomething() = "Hello Hilt!"
}
这里 @Inject constructor()
表示:
- Hilt 需要通过这个构造函数来创建
SomeClass
的实例。 - Hilt 发现
@Inject
后,会自动提供SomeClass
的对象,无需手动创建。
2. 如果不加 constructor
会怎样?
如果尝试这样写:
kotlin
class SomeClass @Inject { // ❌ 语法错误
fun doSomething() = "Hello Hilt!"
}
会直接 编译错误 ,因为 @Inject
必须作用于构造函数,而 constructor
是构造函数的 显式声明方式。
3. constructor
省略的情况
当类没有显式的 constructor
关键字时,它默认是 无参构造函数:
kotlin
class SomeClass {
fun doSomething() = "Hello!"
}
但如果要让 Hilt 注入实例,必须 显式 写 @Inject constructor()
。
4. 带参数的情况
如果 SomeClass
依赖其他类,比如 AnotherClass
,Hilt 也能自动提供:
kotlin
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 的结果
kotlin
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
方法定义
kotlin
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 代码块,并把这个字符串作为参数传递出去。
- 生成一个字符串
执行流程示例
假设:
kotlin
class SomeClass {
fun doSomething(): String = "Hello"
}
class SomeOtherClass {
fun doSomething(): String = "World"
}
val someClass = SomeClass()
val someOtherClass = SomeOtherClass()
那么 doWork
执行后:
kotlin
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 35
defaultConfig {
applicationId "com.test.hiltstudy"
minSdk 24
targetSdk 35
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = '1.8'
}
}
Allow references to generated code
//kapt {
// correctErrorTypes true
//}
dependencies {
implementation libs.androidx.core.ktx
implementation libs.androidx.appcompat
implementation libs.material
implementation libs.androidx.activity
implementation libs.androidx.constraintlayout
testImplementation libs.junit
androidTestImplementation libs.androidx.junit
androidTestImplementation libs.androidx.espresso.core
// Hilt Dependencies
implementation("com.google.dagger:hilt-android:2.51.1")
kapt("com.google.dagger:hilt-android-compiler:2.51.1")
// Fragment KTX for viewModels() delegate
implementation("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")
//retrofit
implementation("com.squareup.retrofit2:converter-gson:2.9.0")
}
📌(2)初始化 Hilt
在 AndroidManifest.xml
:
xml
<application
android:name=".MyApplication"
...>
</application>
然后创建 MyApplication.kt
:
kotlin
@HiltAndroidApp
class MyApplication : Application()
🔹 @HiltAndroidApp
用于初始化 Hilt,它会在 App 启动时配置依赖注入。
📌(3)在 Activity/Fragment 使用 Hilt
在 Activity 里启用 Hilt
kotlin
@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
@Inject
lateinit var someClass: SomeClass // 自动注入
}
🔹 @AndroidEntryPoint
标记 Activity 以支持 Hilt
🔹 @Inject lateinit var someClass: SomeClass
直接注入对象
在 Fragment 里启用 Hilt
kotlin
@AndroidEntryPoint
class MainFragment : Fragment() {
@Inject
lateinit var someRepository: SomeRepository
}
💡 Activity 和 Fragment 都必须加 @AndroidEntryPoint
才能使用 Hilt 注入的对象!
📌(4)在 ViewModel 里使用 Hilt
kotlin
@HiltViewModel
class MainViewModel @Inject constructor(
private val repository: SomeRepository
) : ViewModel() {
fun fetchData() = repository.getData()
}
在 Activity
或 Fragment
里:
kotlin
private val viewModel: MainViewModel by viewModels()
🔹 Hilt 自动创建 MainViewModel
,不用 ViewModelProvider
手动实例化。
📌(5)创建 Hilt 模块(Module)
如果 SomeRepository
不能用 @Inject
直接构造,比如 Retrofit,们需要 使用 Module 提供实例:
kotlin
@Module
@InstallIn(SingletonComponent::class) // 作用于整个应用生命周期
object AppModule {
@Provides
@Singleton
fun provideRetrofit(): Retrofit {
return Retrofit.Builder()
.baseUrl("https://api.example.com/")
.addConverterFactory(GsonConverterFactory.create())
.build()
}
@Provides
@Singleton
fun 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
kotlin
class SomeRepository @Inject constructor() {
fun getData(): String = "Hello from Repository"
}
2️⃣ 在 ViewModel 里注入
kotlin
@HiltViewModel
class MainViewModel @Inject constructor(
private val repository: SomeRepository
) : ViewModel() {
fun fetchData(): String = repository.getData()
}
3️⃣ 在 Activity 里获取数据
kotlin
@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️⃣ 自动管理依赖(对象创建)原理
当写:
kotlin
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 的巨大优势。
✅ 替换方式:
kotlin
@HiltAndroidTest
@UninstallModules(AppModule::class) // 卸载正式模块
class MyTest {
@Module
@InstallIn(SingletonComponent::class)
object TestModule {
@Provides
fun provideFakeApi(): ApiService = FakeApiService()
}
}
✅ 测试时,Hilt 用 TestModule
替换 AppModule
,让测试逻辑而不是网络。
4️⃣ 解耦代码的核心原理
✅ 传统写法(耦合):
kotlin
val repo = MyRepository(ApiService())
MyRepository
硬编码依赖了ApiService
,不利于替换、扩展、测试。
✅ Hilt 写法(解耦):
kotlin
class MyRepository @Inject constructor(private val api: ApiService)
MyRepository
只依赖 抽象接口ApiService
是由外部(Hilt)提供,未来替换为FakeApiService
不用改业务逻辑- 解耦 = 高扩展性 + 高可测试性
🔧 总结原理图(类比管道工厂)
Hilt 功能 | 原理 | 类比 |
---|---|---|
自动注入 | 编译期生成依赖图 | 自动搭建水管连接 |
生命周期管理 | 每个作用域有专属组件管理对象 | 活水池(组件)存在水就流动(对象存活) |
测试替换 | 支持模块替换 | 换水源(Fake)测试流速(逻辑) |
解耦结构 | 依赖抽象、注入实现 | 插拔模块化水管,便于维护 |
🚀 总结一句话:
Hilt = 编译期生成对象工厂 + 生命周期管家 + 解耦利器 + 测试友好助手,让写少但可维护性更高的代码。
六 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 注入依赖 |
+---------------------------+
当然可以!下面是 **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 的依赖 |
🔄 流程示意(依赖注入过程)
plaintext
1. App 启动时,Hilt 生成 SingletonComponent
2. Activity 启动时,注入 ActivityComponent 作用域依赖
3. Fragment 加载时,注入 FragmentComponent 作用域依赖
4. ViewModel 被创建时,注入 ViewModelComponent 中依赖
5. 每个 Component 都可以从其上层 Component 获取依赖
✅ 示例:自动注入过程
kotlin
// 注入 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
)
🧪 测试支持
kotlin
@HiltAndroidTest
@UninstallModules(AppModule::class)
class MyTest {
@Module
@InstallIn(SingletonComponent::class)
object TestModule {
@Provides
fun provideFakeApi(): ApiService = FakeApiService()
}
}
八 Hilt为什么是解耦利器 和测试友好助手
Hilt 之所以被称为 "解耦利器" 和 "测试友好助手" ,是因为它能让 类之间的依赖关系更加松散 ,并且 支持在测试时轻松替换依赖。
我们从 解耦(Decoupling) 和 测试友好(Testability) 两个方面分别解释,并提供示例代码来说明。
🎯 1. Hilt 是如何解耦代码的?
✅ 传统写法(紧耦合,难以扩展)
kotlin
class MyRepository {
private val apiService = ApiService() // 直接创建实例(强依赖)
fun fetchData(): String {
return apiService.getData()
}
}
❌ 问题
MyRepository
直接依赖ApiService
,导致:- 难以替换 (如果要改用
FakeApiService
进行测试,就得修改MyRepository
代码) - 扩展性差 (如果
ApiService
需要不同的实现方式,就得改MyRepository
) - 不适合单元测试(不能注入模拟数据)
- 难以替换 (如果要改用
✅ 使用 Hilt 进行解耦
kotlin
class MyRepository @Inject constructor(private val apiService: ApiService) {
fun fetchData(): String {
return apiService.getData()
}
}
Hilt 提供的 ApiService
实例
kotlin
@Module
@InstallIn(SingletonComponent::class)
object AppModule {
@Provides
@Singleton
fun provideApiService(): ApiService {
return RealApiService()
}
}
✅ 解耦优势
- MyRepository 只依赖
ApiService
抽象 ,不关心ApiService
具体是怎么来的 - 可以轻松替换
ApiService
(比如切换到FakeApiService
进行测试) - 代码更清晰 ,职责更明确(
Hilt
负责管理依赖,不再手动new
)
🔬 2. Hilt 是如何让测试变得更简单的?
Hilt 允许我们在测试时 替换真实依赖,避免复杂的网络请求、数据库操作等,从而更快、更稳定地测试业务逻辑。
✅ 1. 不使用 Hilt,测试困难
kotlin
@Test
fun testFetchData() {
val repo = MyRepository(ApiService()) // 依赖真实的 ApiService
val result = repo.fetchData()
assertEquals("Expected Data", result) // 可能失败,因为是真实数据
}
❌ 问题
MyRepository
无法使用 Mock 依赖- 每次测试都会访问真实 API(影响速度、可能失败)
- 代码可测试性 非常低
✅ 2. 使用 Hilt 轻松替换依赖
🛠️ 在测试中提供 FakeApiService
kotlin
@HiltAndroidTest
@UninstallModules(AppModule::class) // 先卸载正式模块
class MyRepositoryTest {
@Module
@InstallIn(SingletonComponent::class)
object TestModule {
@Provides
fun provideFakeApiService(): ApiService {
return object : ApiService {
override fun getData(): String {
return "Fake Data"
}
}
}
}
@Inject
lateinit var repository: MyRepository
@get:Rule
var hiltRule = HiltAndroidRule(this)
@Before
fun setup() {
hiltRule.inject() // 让 Hilt 注入测试依赖
}
@Test
fun 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 的情况下,我们通常需要手动创建对象:
kotlin
class MyRepository {
private val apiService = ApiService() // 直接创建实例
}
❌ 问题
MyRepository
依赖ApiService
,必须手动new
,不灵活- 如果
ApiService
还依赖Retrofit
,就需要new
多个对象,依赖链复杂
✅ Hilt 作为对象工厂
Hilt 通过 @Module
+ @Provides
或 @Inject
构造注入 自动创建对象:
kotlin
class MyRepository @Inject constructor(private val apiService: ApiService) { }
Hilt 自动提供 ApiService
实例
kotlin
@Module
@InstallIn(SingletonComponent::class)
object AppModule {
@Provides
@Singleton
fun 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 关闭时释放)
❌ 传统方式:手动管理生命周期
kotlin
class MainActivity : AppCompatActivity() {
private val repository = MyRepository(ApiService()) // 手动创建,难以管理
}
❌ 问题
- 全局变量会导致内存泄漏
- Activity 重建(如旋转屏幕)后,数据可能丢失
- 手动管理生命周期非常繁琐
✅ Hilt 自动管理生命周期
Hilt 通过作用域(@InstallIn(Component::class)
)自动匹配生命周期:
🎯 Application 作用域(全局单例)
kotlin
@InstallIn(SingletonComponent::class) // Application 级别
@Module
object AppModule {
@Provides
@Singleton
fun provideMyRepository(apiService: ApiService): MyRepository {
return MyRepository(apiService)
}
}
- 全局单例 :
SingletonComponent
作用域下的对象,整个应用生命周期内存储 - 避免重复创建 :所有使用
MyRepository
的地方,都共享同一个实例
🎯 Activity 作用域(Activity 级别)
kotlin
@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
:
kotlin
@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 就会自动知道如何实例化它。
✅ 示例
kotlin
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 框架创建的,不能使用构造函数注入。
✅ 示例
kotlin
@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
@Inject
lateinit 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
依赖可以拥有不同的生命周期
kotlin
@Singleton
class SomeSingletonClass @Inject constructor()
- 这个
SomeSingletonClass
只会被创建一次,整个应用生命周期都能共享 - Hilt 自动管理它的生命周期,不会意外地创建多个实例
不同作用域示例:
kotlin
@ActivityScoped
class SomeActivityScopedClass @Inject constructor()
ActivityScoped
:每个 Activity 会有自己的实例SingletonComponent
:全局单例ViewModelScoped
:与 ViewModel 生命周期一致
📌 4. @Inject
不能用于接口,需要 @Module
提供
如果 @Inject
用在 接口 上,会报错:
kotlin
interface ApiService {
fun fetchData(): String
}
❌ 直接 @Inject
不行:
kotlin
class ApiServiceImpl @Inject constructor() : ApiService {
override fun fetchData() = "API Data"
}
👉 必须使用 @Module
提供接口实例
kotlin
@Module
@InstallIn(SingletonComponent::class)
object AppModule {
@Provides
fun 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
类上使用
✅ 示例
kotlin
@HiltAndroidApp
class MyApplication : Application()
🚀 关键点
✅ Hilt 会在应用启动时初始化依赖注入
✅ 生成 Hilt_MyApplication
代码,Hilt 依赖的入口
✅ 必须加,否则 Hilt 无法工作
📌 2. @AndroidEntryPoint
作用:
- 用于
Activity
、Fragment
、Service
等,让它们支持 Hilt 依赖注入 - 必须加 ,否则 Hilt 无法注入对象
✅ 示例
kotlin
@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
@Inject
lateinit var someClass: SomeClass // 自动注入
}
🚀 关键点
✅ Activity
、Fragment
、Service
必须加 ,否则 @Inject
不会生效
✅ Hilt 会自动在内部生成依赖注入代码
✅ Fragment 依赖的 Activity 也必须有 @AndroidEntryPoint
,否则会崩溃
📌 3. @Inject
作用:
- 用于 构造函数 或 字段,让 Hilt 知道如何创建和注入对象
✅ 示例
构造函数注入
kotlin
class SomeClass @Inject constructor() {
fun doSomething() = "Hello Hilt!"
}
字段注入
kotlin
@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
@Inject
lateinit var someClass: SomeClass
}
🚀 关键点
✅ 构造函数上加 @Inject
,Hilt 知道如何创建对象
✅ 字段加 @Inject
,Hilt 会自动提供依赖
📌 4. @Module
+ @InstallIn
作用:
- 提供无法直接
@Inject
的对象(如接口、第三方库) - 定义依赖的作用域
✅ 示例
kotlin
@Module
@InstallIn(SingletonComponent::class) // 全局单例
object AppModule {
@Provides
@Singleton
fun 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
的情况,如第三方库)
✅ 示例
kotlin
@Module
@InstallIn(SingletonComponent::class)
object AppModule {
@Provides
fun provideSomeClass(): SomeClass {
return SomeClass()
}
}
🚀 关键点
✅ 如果 SomeClass
不能 @Inject
,就用 @Provides
提供
✅ 返回类型就是 Hilt 提供的类型
📌 6. @Binds
作用:
- 用于接口的实现绑定
- 比
@Provides
更高效(少了一次方法调用)
✅ 示例
kotlin
interface ApiService {
fun fetchData(): String
}
class ApiServiceImpl @Inject constructor() : ApiService {
override fun fetchData() = "API Data"
}
@Module
@InstallIn(SingletonComponent::class)
abstract class AppModule {
@Binds
abstract fun bindApiService(impl: ApiServiceImpl): ApiService
}
🚀 关键点
✅ @Binds
只能用于 abstract
方法,不能有逻辑
✅ 比 @Provides
更高效,但 @Provides
更灵活
📌 7. 作用域注解
作用:
- 控制 对象的生命周期,防止重复创建
- 适用于
@Provides
或@Inject
提供的对象
✅ 示例
kotlin
@Singleton
class SomeSingletonClass @Inject constructor()
作用域 | 适用范围 | 生命周期 |
---|---|---|
SingletonComponent |
全局 | 应用生命周期 |
ActivityRetainedComponent |
ViewModel | Activity 重新创建时依然存在 |
ActivityComponent |
Activity | Activity 销毁时释放 |
FragmentComponent |
Fragment | Fragment 销毁时释放 |
📌 8. @HiltViewModel
作用:
- 让 ViewModel 支持 Hilt 注入
- 简化 ViewModel 创建
✅ 示例
kotlin
@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
✅ 示例
kotlin
@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
等注解,让 对象创建 & 生命周期管理 自动化,大幅减少样板代码,提高可维护性!