欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 新闻 > 会展 > Android Hilt 教程

Android Hilt 教程

2025/4/5 18:33:00 来源:https://blog.csdn.net/qq_26296197/article/details/146989195  浏览:    关键词: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 插件:

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 开发。

按照本教程,已经学会如何在 ActivityViewModel普通类 中使用 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 -> ... }
  • resultdoWork 方法执行完成后,回传的结果
  • 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 是什么?

  • resultdoWork 任务执行后,回传给 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

总结

  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 开发中,们通常需要手动创建和管理对象,例如 ViewModelRepositoryRetrofit 等。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()
}

ActivityFragment 里:

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注解

  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. 手动创建 SomeRepositoryMainViewModel 的实例
  3. 实现自定义的 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 的共享依赖
@ViewModelScopedViewModel 生命周期当前 ViewModel 独享的对象
✅ 原理:

Hilt 在每个作用域下生成一个 Dagger 组件(Component):

  • SingletonComponent 对应 Application
  • ActivityComponent 对应 Activity
  • ViewModelComponent 对应 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 注入依赖       |+---------------------------+

🧩 作用域绑定关系

组件类型对应生命周期示例依赖项
SingletonComponentApplication 全局单例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,导致:
    1. 难以替换(如果要改用 FakeApiService 进行测试,就得修改 MyRepository 代码)
    2. 扩展性差(如果 ApiService 需要不同的实现方式,就得改 MyRepository
    3. 不适合单元测试(不能注入模拟数据)

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

解耦优势

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

测试优势

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

🎯 关键点

  1. Hilt 自动创建 ApiService,并将其注入到 MyRepository
  2. 我们不需要手动 new,Hilt 充当工厂,自动提供对象
  3. 如果 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 自动匹配对象生命周期
测试支持需要大量 MockHilt 允许轻松替换依赖

💡 总结

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)用于 在类内部自动注入依赖对象,通常用于 ActivityFragmentViewModel,因为它们的实例是由 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!"}
}

📌 关键点

  1. @Inject lateinit var someClass: SomeClass
    • 让 Hilt 自动创建 SomeClass 实例,并注入到 someClass 变量中
    • 不需要 new SomeClass()
  2. @AndroidEntryPoint 必须加在 ActivityFragment,否则 Hilt 无法注入
    • 这是因为 ActivityFragment 由 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 varActivity、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

作用

  • 用于 ActivityFragmentService,让它们支持 Hilt 依赖注入
  • 必须加,否则 Hilt 无法注入对象

✅ 示例

@AndroidEntryPoint
class MainActivity : AppCompatActivity() {@Injectlateinit var someClass: SomeClass  // 自动注入
}

🚀 关键点

ActivityFragmentService 必须加,否则 @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全局应用生命周期
ActivityRetainedComponentViewModelActivity 重新创建时依然存在
ActivityComponentActivityActivity 销毁时释放
FragmentComponentFragmentFragment 销毁时释放

📌 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 必须配合 @AndroidEntryPointActivityFragment
ViewModel 不用手动创建,Hilt 自动管理


📌 9. @EntryPoint

作用

  • 用于无法使用 @AndroidEntryPoint 的类
  • ContentProviderBroadcastReceiver

✅ 示例

@EntryPoint
@InstallIn(SingletonComponent::class)
interface MyEntryPoint {fun getSomeClass(): SomeClass
}// 使用 EntryPoint
val someClass = EntryPointAccessors.fromApplication(context, MyEntryPoint::class.java).getSomeClass()

🚀 关键点

适用于无法直接 @Inject 的情况
Hilt 依然能提供依赖


🎯 结论

注解作用
@HiltAndroidApp让 Hilt 初始化依赖注入(必须加在 Application
@AndroidEntryPointActivityFragmentService 支持 Hilt
@Inject构造函数注入 & 字段注入
@Module + @InstallIn提供无法 @Inject 的依赖
@Provides直接提供实例(如第三方库)
@Binds绑定接口实现(比 @Provides 高效)
@Singleton作用域管理(全局单例)
@HiltViewModelHilt ViewModel 支持
@EntryPoint用于 BroadcastReceiverContentProvider

🚀 总结:Hilt 通过 @Inject@Module@Provides 等注解,让 对象创建 & 生命周期管理 自动化,大幅减少样板代码,提高可维护性!

参考

google Hilt 教程

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词