Android Hilt 笔记

🎯 为什么需要 Hilt?从 Dagger 的痛点说起

在 Hilt 出现之前,Android 开发者如果要使用依赖注入,最强大的选择是 Dagger。但 Dagger 的学习曲线非常陡峭,配置也相当复杂。你需要手动创建和管理大量的 ComponentModule,还要理解复杂的作用域和依赖关系图。很多开发者在使用 Dagger 的过程中,常常会陷入自己搭建的"依赖迷宫"中,难以调试和维护。

Google 推出 Hilt 的目的,就是在不改变 Dagger 强大能力的前提下,针对 Android 开发场景进行"固化"和"简化"。它通过预定义好 Android 应用中的常用组件(如 Application、Activity、Fragment 等)及其生命周期,让开发者无需手动创建和管理这些组件,只需通过注解即可完成依赖注入。


🧱 Hilt 的核心概念

在开始使用之前,你需要理解 Hilt 的三大核心构件:

  1. 组件 (Components) :Hilt 为 Android 中的每个标准类(Application、Activity、Fragment、Service、View 等)都预定义了一个对应的 Dagger 组件。这些组件负责管理依赖的生命周期,并自动跟随 Android 组件的生命周期创建和销毁。例如:

    • SingletonComponent:伴随 Application 的生命周期。
    • ActivityComponent:伴随 Activity 的生命周期。
    • FragmentComponent:伴随 Fragment 的生命周期。
    • ViewModelComponent:伴随 ViewModel 的生命周期。
  2. 作用域 (Scopes) :通过注解(如 @Singleton@ActivityScoped)来限定依赖在对应组件内的唯一性。例如,用 @Singleton 标记的依赖在全局只有一个实例,而用 @ActivityScoped 标记的依赖在同一个 Activity 内是同一个实例。

  3. 模块 (Modules) :当你想注入的类不是你自己写的(如 Retrofit、OkHttpClient 等第三方库),或者是一个接口时,就需要用到 Hilt 模块。它是一个用 @Module 注解的类,你需要在这个类中定义如何提供这些依赖的实例。同时,必须用 @InstallIn 注解指明这个模块安装在哪个组件中,从而让 Hilt 知道它的作用范围。


🚀 Hilt 的使用方法

1. 添加依赖与配置

首先,在项目根目录的 build.gradle 文件中添加 Hilt 的 Gradle 插件:

gradle 复制代码
buildscript {
    ext {
        hilt_version = '2.48' // 请使用最新版本
    }
    dependencies {
        classpath "com.google.dagger:hilt-android-gradle-plugin:$hilt_version"
    }
}

然后在你的 app 模块的 build.gradle 文件中应用插件并添加依赖:

gradle 复制代码
plugins {
    id 'kotlin-kapt'
    id 'dagger.hilt.android.plugin'
}

android {
    // ...
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
    kotlinOptions {
        jvmTarget = '1.8'
    }
}

dependencies {
    implementation "com.google.dagger:hilt-android:$hilt_version"
    kapt "com.google.dagger:hilt-compiler:$hilt_version"
}

kotlin-kapt 插件用于处理 Hilt 的编译时注解,这是代码生成的基础。

2. 初始化 Hilt:@HiltAndroidApp

所有使用 Hilt 的应用都必须有一个带有 @HiltAndroidApp 注解的 Application 类。这会触发 Hilt 的代码生成,生成一个全局的 Dagger 组件(即 SingletonComponent)的基类。

kotlin 复制代码
@HiltAndroidApp
class MyApplication : Application() {
    override fun onCreate() {
        super.onCreate()
        // 应用初始化
    }
}

别忘了在 AndroidManifest.xml 中注册这个自定义的 Application 类。

3. 在 Android 类中注入:@AndroidEntryPoint

要在 Activity、Fragment、View、Service、BroadcastReceiver 中使用依赖注入,你需要为它们添加 @AndroidEntryPoint 注解。

kotlin 复制代码
@AndroidEntryPoint
class MainActivity : AppCompatActivity() {

    // 通过 @Inject 注解的字段,声明需要注入的依赖
    @Inject
    lateinit var userRepository: UserRepository

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        // 此时 userRepository 已经被自动注入了,可以直接使用
        println(userRepository.getUser())
    }
}

需要注意的是,如果给 Fragment 添加了 @AndroidEntryPoint,那么依赖它的所有 Activity 也必须添加 @AndroidEntryPoint

4. 提供依赖的三种方式

Hilt 提供了三种主要的方式来告诉它"如何创建某个类的实例"。

A. 构造函数注入:@Inject

对于你自己编写的、可以修改构造函数的类,这是最简单的方式。直接在类的构造函数上添加 @Inject 注解。

kotlin 复制代码
// 告诉 Hilt:当需要 UserRepository 时,就通过这个构造函数创建它
class UserRepository @Inject constructor() {
    fun getUser(): String = "User Data"
}

B. 模块 + @Provides

对于你无法修改构造函数的类(如 Retrofit、OkHttpClient 等第三方库),或者需要复杂构建逻辑的情况,你需要创建一个 Hilt 模块,并在其中定义一个用 @Provides 注解的方法。方法的返回值就是要提供的类型,Hilt 会调用这个方法获取实例。

kotlin 复制代码
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory
import javax.inject.Singleton

@Module
@InstallIn(SingletonComponent::class) // 告知此模块安装在全局单例组件中
object NetworkModule {

    @Provides
    @Singleton // 标记为全局单例
    fun provideRetrofit(): Retrofit {
        return Retrofit.Builder()
            .baseUrl("https://api.example.com/")
            .addConverterFactory(GsonConverterFactory.create())
            .build()
    }
}

C. 模块 + @Binds

当你想为一个接口 提供具体的实现类时,使用 @Binds@Binds 注解的方法是一个抽象方法,参数是接口的实现类,返回值是接口本身。它告诉 Hilt,当需要接口时,就使用这个实现类。

kotlin 复制代码
// 定义接口
interface UserService {
    fun getUser(): String
}

// 实现类,用 @Inject 标记其构造函数,以便 Hilt 能创建它
class UserServiceImpl @Inject constructor() : UserService {
    override fun getUser(): String = "User from service"
}

// 在模块中绑定接口与实现
@Module
@InstallIn(SingletonComponent::class)
abstract class ServiceModule {

    @Binds
    abstract fun bindUserService(impl: UserServiceImpl): UserService
}

5. 限定符:@Qualifier

当同一个类型需要提供多个不同的实例时,就需要用到限定符。例如,你可能需要两个不同的 Retrofit 实例(一个用于 GitHub API,一个用于其他 API)。Hilt 内置了 @ApplicationContext@ActivityContext 两个限定符来区分不同的 Context。你也可以自定义限定符。

kotlin 复制代码
// 定义两个限定符注解
@Qualifier
@Retention(AnnotationRetention.BINARY)
annotation class GithubRetrofit

@Qualifier
@Retention(AnnotationRetention.BINARY)
annotation class OtherRetrofit

@Module
@InstallIn(SingletonComponent::class)
object NetworkModule {

    @GithubRetrofit
    @Provides
    @Singleton
    fun provideGithubRetrofit(): Retrofit {
        return Retrofit.Builder().baseUrl("https://api.github.com/").build()
    }

    @OtherRetrofit
    @Provides
    @Singleton
    fun provideOtherRetrofit(): Retrofit {
        return Retrofit.Builder().baseUrl("https://other.api.com/").build()
    }
}

// 使用时,用对应的限定符注解字段
@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
    @Inject
    @GithubRetrofit
    lateinit var githubRetrofit: Retrofit

    @Inject
    @OtherRetrofit
    lateinit var otherRetrofit: Retrofit
}

6. 为 ViewModel 注入:@HiltViewModel

Hilt 与 Jetpack ViewModel 的集成非常出色。你只需在 ViewModel 上添加 @HiltViewModel 注解,并在其构造函数中使用 @Inject,Hilt 就会自动帮你创建 ViewModel,并注入所需的依赖。你的 Activity/Fragment 无需再关心如何创建 ViewModel。

kotlin 复制代码
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import dagger.hilt.android.lifecycle.HiltViewModel
import javax.inject.Inject

@HiltViewModel
class MainViewModel @Inject constructor(
    private val userRepository: UserRepository
) : ViewModel() {

    fun loadUser() {
        // 使用 userRepository 获取数据
    }
}

在 Activity 或 Fragment 中,你就可以像往常一样通过 by viewModels()hiltNavGraphViewModels 获取 ViewModel 实例了,无需任何额外的工厂类。


🧪 Hilt 的测试支持

Hilt 的强大之处也体现在测试上。它提供了专门的测试 API,让你能轻松地在单元测试和插桩测试中替换依赖,实现隔离测试。

  1. 配置测试 :为测试类添加 @HiltAndroidTest 注解和一个 HiltAndroidRule,并使用 HiltTestApplication 作为测试的 Application。
  2. 注入依赖 :在测试类中,你可以像在生产代码中一样使用 @Inject 来注入所需的对象。
  3. 替换模块 :Hilt 提供了 @TestInstallIn@UninstallModules 两种方式来替换生产代码中的模块。
    • @TestInstallIn:用于定义一个全局的测试模块,替换掉整个生产模块。适合大多数测试场景。
    • @UninstallModules:用于在单个测试类中,临时卸载某些生产模块,然后通过 @BindValue 等方式提供模拟实现。这种方式非常灵活,但可能会导致构建时间变慢,应谨慎使用。
kotlin 复制代码
@HiltAndroidTest
@RunWith(AndroidJUnit4::class)
class MainActivityTest {

    @get:Rule
    val hiltRule = HiltAndroidRule(this)

    @Inject
    lateinit var userRepository: UserRepository // 注入真实或模拟的对象

    @Before
    fun setup() {
        hiltRule.inject() // 执行注入
    }

    // ... 测试代码
}

⚙️ 原理简析:Hilt 是如何工作的?

Hilt 的核心原理是编译时注解处理与代码生成

  1. 注解处理 :在编译时,Hilt 的注解处理器会扫描所有带有 @HiltAndroidApp@AndroidEntryPoint@Module@Inject 等注解的代码。
  2. 生成 Dagger 组件 :基于扫描结果,Hilt 会自动生成一系列标准的 Dagger 组件(如 SingletonComponentActivityComponent 等),这些组件已经预先配置好了与 Android 组件生命周期的绑定关系。例如,ActivityComponent 会在 Activity 的 onCreate() 时创建,在 onDestroy() 时销毁。
  3. 生成代码 :Hilt 还会为被 @AndroidEntryPoint 标记的类生成一个基类。这个基类负责在合适的生命周期回调中(如 onCreate())执行实际的注入操作,即从对应的 Dagger 组件中获取依赖,并赋值给带有 @Inject 注解的字段。
  4. 无运行时反射:所有这些工作都在编译时完成,生成的代码是纯粹的、高效的 Dagger 代码,因此在运行时几乎没有性能损耗。

💡 总结与最佳实践

  • Hilt = Dagger 的 Android 最佳实践:它不是为了取代 Dagger,而是为了让开发者能以更低的成本享受到 Dagger 的强大能力。
  • 几乎零模板代码 :你不再需要编写繁琐的 Component 和工厂类,只需关注如何用注解声明依赖关系。
  • 与 Jetpack 深度集成:对 ViewModel、Navigation、Compose 等都有很好的支持。
  • 测试友好:提供了强大的测试 API,让依赖替换变得异常简单。
  • 性能优化技巧 :合理使用作用域注解(如 @Singleton)来管理对象生命周期;在模块设计上保持"单一职责",便于测试替换;对于大量测试,优先使用 @TestInstallIn 来提升构建速度。

Hilt 的出现,极大地改善了 Android 开发的依赖注入体验。希望这篇讲解能帮助你更好地理解和使用 Hilt,写出更健壮、更可维护的代码。

相关推荐
醉饮千觞不知愁2 小时前
Android Lifecycle的事件与状态映射关系
android·kotlin
千里马学框架3 小时前
app性能优化:优化布局层次结构
android·面试·性能优化·framework·分屏·布局·小米汽车
dustcell.3 小时前
高性能web服务器
android·服务器·前端
zh_xuan3 小时前
React Native Demo
android·javascript·react native·ts
zh_xuan3 小时前
kotlin 挂起函数2
android·kotlin·挂起函数
kyle~3 小时前
MySQL基础知识点与常用SQL语句整理
android·sql·mysql
XiaoLeisj4 小时前
Android RecyclerView 实战:从基础列表到多类型 Item、分割线与状态复用问题
android·java
zh_xuan4 小时前
kotlin async异步协程构建器
android·kotlin·协程
阿林来了4 小时前
Flutter三方库适配OpenHarmony【flutter_web_auth】— Android 端 Chrome Custom Tabs 实现分析
android·chrome·flutter