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 教程