Android Hilt 教程

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

总结

  1. doWork 方法执行后,someClass.doSomething() 和 someOtherClass.doSomething() 生成两个字符串。
  2. callback("$result1, $result2") 将它们拼接,并调用外部 Lambda 代码块,把结果传出去。
  3. result 是 Lambda 代码块接收的参数,即 "Hello, World" 这样的字符串。
  4. 在 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注解

  1. 当在 MainActivity 中使用 by viewModels() 时,Android 系统需要创建 MainViewModel 的实例
  2. MainViewModel 的构造函数需要一个 SomeRepository 参数
  3. 由于 SomeRepository 使用了 @Inject 注解,它只能通过 Hilt 的依赖注入系统来创建和管理

这就造成了一个依赖链:

  • MainActivity 需要 MainViewModel
  • MainViewModel 需要 SomeRepository
  • SomeRepository 由 Hilt 管理

所以当使用了 Hilt 来管理某个依赖(如 SomeRepository)时,所有需要使用这个依赖的类(如 MainViewModel)也必须通过 Hilt 来管理。这不是"强行绑定",而是依赖注入系统工作的必然要求。

如果不想使用 Hilt,需要:

  1. 移除 SomeRepository 的 @Inject 注解
  2. 手动创建 SomeRepository 和 MainViewModel 的实例
  3. 实现自定义的 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 对应 Application
  • ActivityComponent 对应 Activity
  • ViewModelComponent 对应 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,导致:
    1. 难以替换 (如果要改用 FakeApiService 进行测试,就得修改 MyRepository 代码)
    2. 扩展性差 (如果 ApiService 需要不同的实现方式,就得改 MyRepository)
    3. 不适合单元测试(不能注入模拟数据)

✅ 使用 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()
    }
}

✅ 解耦优势

  1. MyRepository 只依赖 ApiService 抽象 ,不关心 ApiService 具体是怎么来的
  2. 可以轻松替换 ApiService (比如切换到 FakeApiService 进行测试)
  3. 代码更清晰 ,职责更明确(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% 可预测的测试结果
    }
}

✅ 测试优势

  1. 自动替换真实依赖(不再访问网络或数据库)
  2. 测试速度更快(不依赖外部服务)
  3. 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)
    }
}

🎯 关键点

  1. Hilt 自动创建 ApiService,并将其注入到 MyRepository
  2. 我们不需要手动 new,Hilt 充当工厂,自动提供对象
  3. 如果 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!"
    }
}

📌 关键点

  1. @Inject lateinit var someClass: SomeClass
    • 让 Hilt 自动创建 SomeClass 实例,并注入到 someClass 变量中
    • 不需要 new SomeClass()
  2. @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 等注解,让 对象创建 & 生命周期管理 自动化,大幅减少样板代码,提高可维护性!

参考

google Hilt 教程

相关推荐
千里马学框架4 天前
一起学 Android 14:ShellTransition 屏幕旋转过程深度剖析
android·智能手机·性能优化·framework·性能·屏幕旋转·rotation
美狐美颜SDK开放平台4 天前
开发直播APP时如何接入视频美颜SDK?开发流程与注意事项
android·人工智能·计算机视觉·音视频·直播美颜sdk
AFinalStone4 天前
Android7 SystemUI源码解析(七)Keyguard锁屏模块深度解析
android·systemui
致远ccc4 天前
Google Play 上架前如何测试 App?多国家 Android 环境测试
android·app测试·googleplay·多国家应用测试
ttyyttemo4 天前
Kotlin 协程中的 Job 结构化并发与取消
android
sun0077004 天前
tbox 4g/5g切换,导致wan ip 改变,导致车机旧网络不可用。需要重启车机才行
android
其实防守也摸鱼4 天前
内网穿透与反向代理:原理、工具与实战指南
android·大数据·运维·安全·网络安全·自动化·渗透
AFinalStone4 天前
Android7 SystemUI 源码解析(四)NavigationBar 导航栏与 SystemBars
android·systemui
JMchen4 天前
属性动画原理与高级动画实现
android·kotlin·canvas
AFinalStone4 天前
Android7 SystemUI 源码解析(二)启动流程深度解析
android·systemui