Android-开源框架和源码分析-07-Hilt-源码解析

327 阅读10分钟

Hilt 使用简介

Hilt 是 Google 提供的官方依赖注入(DI)框架,基于 Dagger 2 构建,专为 Android 开发设计。它简化了依赖注入的配置和使用,自动为 Android 组件(如 ActivityFragmentViewModel)提供所需的依赖,使得代码更加清晰、模块化和易于测试。

以下是使用 Hilt 的基本步骤和示例:


1. 在项目中集成 Hilt

首先,你需要在 Android 项目的 build.gradle 文件中添加 Hilt 的依赖。

1.1 在项目根目录 build.gradle 中添加 Hilt 插件

gradle
复制编辑
buildscript {
    repositories {
        google()
        mavenCentral()
    }
    dependencies {
        classpath("com.google.dagger:hilt-android-gradle-plugin:2.40.5")
    }
}

1.2 在应用模块 build.gradle 中应用 Hilt 插件和添加依赖

gradle
复制编辑
plugins {
    id 'com.android.application'
    id 'dagger.hilt.android.plugin'  // 使用 Hilt 插件
}

android {
    compileSdkVersion 30
    defaultConfig {
        applicationId "com.example.hiltapp"
        minSdkVersion 21
        targetSdkVersion 30
        versionCode 1
        versionName "1.0"
    }
}

dependencies {
    implementation 'com.google.dagger:hilt-android:2.40.5'  // Hilt 核心库
    kapt 'com.google.dagger:hilt-compiler:2.40.5'  // Hilt 编译器
}
  • hilt-android 是 Hilt 的核心库,提供依赖注入功能。
  • hilt-compiler 是注解处理器,生成必要的 Dagger 代码。

1.3 启用 Kapt(Kotlin 注解处理器)

如果你使用 Kotlin,需要启用 Kapt 以支持 Hilt 生成代码:

gradle
复制编辑
apply plugin: 'kotlin-kapt'

2. Hilt 的基本使用

2.1 在 Application 类中启用 Hilt

使用 @HiltAndroidApp 注解标记 Application 类,Hilt 会自动在应用启动时初始化。

kotlin
复制编辑
@HiltAndroidApp
class MyApplication : Application() {
    override fun onCreate() {
        super.onCreate()
        // Hilt 初始化
    }
}
  • @HiltAndroidApp 是 Hilt 提供的一个注解,用于启动依赖注入功能。

2.2 提供依赖

Hilt 使用 @Module@InstallIn 注解提供依赖。通过 @Provides 注解的方法告诉 Hilt 你需要的依赖是如何创建的。

例如,创建一个提供网络服务的模块:

kotlin
复制编辑
@Module
@InstallIn(SingletonComponent::class)  // 单例范围
object NetworkModule {

    @Provides
    @Singleton  // 提供单例服务
    fun provideApiService(): ApiService {
        return ApiService()
    }
}
  • @Module:标记一个类为依赖模块。
  • @InstallIn(SingletonComponent::class) :表示依赖的作用域为单例级别,即整个应用生命周期内有效。
  • @Provides:标记方法提供依赖对象。

2.3 注入依赖

通过 @Inject 注解,Hilt 会自动注入需要的依赖。

kotlin
复制编辑
@HiltAndroidApp
class MyApplication : Application() {

    @Inject lateinit var apiService: ApiService

    override fun onCreate() {
        super.onCreate()
        // 在这里可以使用 apiService
    }
}
  • @Inject:用于标记需要被注入的字段,Hilt 会自动提供该依赖。

2.4 在 ActivityFragment 中注入依赖

ActivityFragment 中,你可以使用 @AndroidEntryPoint 注解来标记需要依赖注入的组件。然后,使用 by inject()by viewModels() 来获取依赖。

kotlin
复制编辑
@AndroidEntryPoint
class MainActivity : AppCompatActivity() {

    @Inject lateinit var apiService: ApiService

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        // 使用注入的 apiService
        apiService.getData()
    }
}
  • @AndroidEntryPoint:标记 ActivityFragment,表示 Hilt 会为它们进行依赖注入。
  • @Inject:标记依赖字段,Hilt 会自动注入它。

2.5 使用 ViewModel@HiltViewModel 注解

ViewModel 是 Android 中用于管理 UI 数据的组件。通过 Hilt,我们可以将依赖注入到 ViewModel 中。

kotlin
复制编辑
@HiltViewModel
class MyViewModel @Inject constructor(private val apiService: ApiService) : ViewModel() {

    fun fetchData() {
        apiService.getData()
    }
}
  • @HiltViewModel:将 ViewModel 标记为 Hilt 管理的组件,支持依赖注入。
  • @Inject:标记构造函数,以便 Hilt 自动注入依赖。

ActivityFragment 中使用 by viewModels() 来获取 ViewModel

kotlin
复制编辑
@AndroidEntryPoint
class MainActivity : AppCompatActivity() {

    private val myViewModel: MyViewModel by viewModels()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        myViewModel.fetchData()
    }
}
  • by viewModels() :用于获取 ViewModel 实例并自动注入。

3. 进阶使用

3.1 作用域管理(Scope Management)

Hilt 提供了不同的作用域,允许开发者为依赖指定生命周期范围。常见的作用域有:

  • SingletonComponent:应用级单例,整个应用生命周期内有效。
  • ActivityComponentActivity 生命周期内有效。
  • FragmentComponentFragment 生命周期内有效。
kotlin
复制编辑
@Module
@InstallIn(ActivityComponent::class)  // 依赖与 Activity 生命周期相关
object ActivityModule {

    @Provides
    fun provideActivityDependency(): ActivityDependency {
        return ActivityDependency()
    }
}
  • @InstallIn(ActivityComponent::class) 表示该依赖与 Activity 生命周期绑定,Activity 销毁时,依赖也会被销毁。

3.2 使用 @Qualifier 注解

当多个依赖类型相同但实例不同的时候,可以使用 @Qualifier 来区分它们。

kotlin
复制编辑
@Qualifier
@Retention(AnnotationRetention.BINARY)
annotation class NetworkClient

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

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

    @Provides
    @NetworkClient
    fun provideNetworkClient(): OkHttpClient {
        return OkHttpClient.Builder().build()
    }

    @Provides
    @DatabaseClient
    fun provideDatabaseClient(): OkHttpClient {
        return OkHttpClient.Builder().build()
    }
}
  • 使用 @NetworkClient@DatabaseClient 来区分不同的 OkHttpClient 实例。

3.3 @AssistedInject 和动态参数

有时候 ViewModel 等需要动态参数,不能通过构造函数注入。这时可以使用 @AssistedInject

kotlin
复制编辑
@AssistedInject
class MyViewModel @AssistedInject constructor(
    private val savedStateHandle: SavedStateHandle,
    @Assisted private val parameter: String
) : ViewModel() {
    // 动态参数 parameter
}
  • @AssistedInject 用于支持在运行时传递参数的注入。

4. 总结

Hilt 是一个强大的依赖注入框架,简化了 Android 中依赖注入的过程。通过使用 Hilt,开发者可以:

  • 自动管理依赖:通过 @Inject 自动注入所需的依赖。
  • 生命周期管理:Hilt 可以根据组件的生命周期自动管理依赖的创建与销毁。
  • 简化代码:通过注解和生成的代码,避免手动管理和配置复杂的依赖关系。
  • 更好的测试性:Hilt 提供了便捷的测试功能,可以轻松模拟和注入依赖,进行单元测试。

通过合理使用 Hilt,可以让 Android 应用的架构更清晰、更解耦,并提高代码的可维护性和可测试性。

Hilt 源码分析

Hilt 是 Android 官方推荐的依赖注入(DI)框架,基于 Dagger 2 构建,它简化了 Dagger 的使用并自动化了很多配置,使得开发者能够更轻松地进行依赖注入。Hilt 提供了一个用于 Android 开发的专用 DI 解决方案,能够有效管理组件间的依赖关系,并使得代码更加清晰和可维护。

Hilt 的核心是 Dagger,但它通过一系列的 注解处理器,简化了 Dagger 2 的复杂性,并与 Android 架构组件(如 ActivityFragmentViewModel)紧密集成。本文将详细分析 Hilt 的源码实现原理,帮助更好地理解它是如何工作的。


1. Hilt 的核心组件

Hilt 核心组件主要由以下几个部分组成:

  1. Hilt 注解

    • @HiltAndroidApp
    • @AndroidEntryPoint
    • @HiltViewModel
    • @InstallIn
    • @Provides@Inject
  2. 模块和组件

    • 模块(Module) :用于提供依赖的生成方法。
    • 组件(Component) :由 Hilt 和 Dagger 生成,用于注入依赖。
  3. 生命周期管理

    • Hilt 管理的生命周期组件,如 ActivityComponentFragmentComponentSingletonComponent 等。

2. Hilt 注解和工作流程

Hilt 主要依赖于注解处理器在编译时生成代码。通过一些注解,Hilt 自动化了很多 Dagger 2 配置的部分,简化了注入的过程。

2.1 @HiltAndroidApp

@HiltAndroidApp 注解用于标记 Application 类,Hilt 会在该类的 onCreate() 中进行初始化。这是 Hilt 的启动点。

kotlin
复制编辑
@HiltAndroidApp
class MyApplication : Application() {
    override fun onCreate() {
        super.onCreate()
        // Hilt 初始化
    }
}

@HiltAndroidApp 注解应用到 Application 类时,Hilt 会自动生成一个基础类,用来初始化 DI 环境。

2.2 @InstallIn 和生命周期组件

@InstallIn 注解用于指示依赖应该安装在哪个生命周期组件中。它指定了某个模块的作用域(如单例或在 ActivityFragment 生命周期内)。

kotlin
复制编辑
@Module
@InstallIn(SingletonComponent::class)
object AppModule {
    @Provides
    fun provideNetworkClient(): OkHttpClient {
        return OkHttpClient.Builder().build()
    }
}
  • SingletonComponent:模块在应用生命周期内有效。
  • ActivityComponent:模块在 Activity 生命周期内有效。
  • FragmentComponent:模块在 Fragment 生命周期内有效。

Hilt 会根据这些作用域自动管理依赖的生命周期,确保依赖的创建和销毁与 Android 组件的生命周期一致。

2.3 @HiltViewModel@Inject

@HiltViewModel 注解用于为 ViewModel 提供依赖注入。Hilt 会根据 ViewModel 的生命周期管理它的依赖,并通过 @Inject 注解自动将依赖注入到 ViewModel 的构造函数中。

kotlin
复制编辑
@HiltViewModel
class MyViewModel @Inject constructor(
    private val repository: MyRepository
) : ViewModel() {
    // 使用注入的 repository
}

Hilt 会自动生成代码来提供这些依赖,并确保 ViewModel 的生命周期正确。

2.4 依赖注入工作流程

  1. 模块注册

    • 使用 @Module@InstallIn 定义模块,并通过 @Provides 注解提供依赖。
  2. 依赖注入

    • 在需要依赖的类中,使用 @Inject 注解标记构造函数或字段,Hilt 会自动处理依赖注入。
  3. 生命周期管理

    • Hilt 根据 @InstallIn 注解的生命周期管理依赖的创建和销毁。它使用不同的生命周期组件来控制依赖的生命周期。

3. Hilt 和 Dagger 的关系

Hilt 构建于 Dagger 之上,实际上,Hilt 只是对 Dagger 进行了一层抽象,简化了它的使用流程。Dagger 是一个编译时的依赖注入框架,而 Hilt 使用了 Dagger 提供的功能,并为 Android 提供了一些特定的 API(如 @HiltViewModel@AndroidEntryPoint 等)。

3.1 组件和模块生成

Hilt 使用 Dagger 生成组件和模块。每个通过 @InstallIn 标记的模块都会为不同的生命周期生成一个 Dagger 组件。这些组件在应用的生命周期内管理依赖的注入。

例如,SingletonComponent 是为应用生命周期生成的组件,确保所有在该作用域下的依赖是单例的:

kotlin
复制编辑
@InstallIn(SingletonComponent::class)
@Module
object AppModule {
    @Provides
    @Singleton
    fun provideNetworkClient(): OkHttpClient {
        return OkHttpClient.Builder().build()
    }
}

3.2 Dagger 代码生成

Hilt 在编译时生成 Dagger 代码,通过注解处理器(@HiltAndroidApp@AndroidEntryPoint 等)为我们自动创建 Dagger 的组件和依赖注入代码。开发者无需手动配置 Dagger 组件,Hilt 会根据生命周期自动管理它们。

Hilt 会生成一个基类 Hilt_MyActivity,负责管理 Activity 中的依赖注入:

kotlin
复制编辑
class MyActivity : AppCompatActivity() {
    @Inject lateinit var networkClient: OkHttpClient

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        // Hilt 会自动注入依赖
    }
}

Hilt 会生成类似以下的代码:

kotlin
复制编辑
class MyActivity_HiltInjector {
    static void inject(MyActivity activity) {
        // 注入 networkClient
        activity.networkClient = networkClientProvider.get()
    }
}

4. Hilt 的工作原理

  1. 注解处理

    • Hilt 使用注解处理器(HiltProcessor)在编译时生成所有必要的代码。@HiltAndroidApp@InstallIn@Provides 等注解将触发生成组件、模块、依赖注入代码。
  2. 生成组件

    • Hilt 会为每个 @InstallIn 注解的模块生成一个 Dagger 组件。这些组件负责管理依赖的创建、生命周期以及注入。
  3. 自动注入

    • @Inject 注解应用到字段或构造函数时,Hilt 会为我们自动注入依赖。它通过生成 Dagger 组件来注入所需的依赖,并确保每个依赖的生命周期与 ActivityFragmentViewModel 等组件一致。
  4. 生命周期管理

    • Hilt 会根据作用域(如 SingletonComponentActivityComponent 等)来管理依赖的生命周期。它会确保在依赖的生命周期结束时,自动销毁和清理这些依赖。

5. Hilt 源码关键部分

5.1 @HiltAndroidApp 注解处理器

当我们在 Application 类中使用 @HiltAndroidApp 注解时,Hilt 会生成一个基础类,负责初始化 Hilt 环境。

kotlin
复制编辑
@HiltAndroidApp
class MyApplication : Application() {
    override fun onCreate() {
        super.onCreate()
        // Hilt 初始化
    }
}

Hilt 会在编译时生成如下代码:

kotlin
复制编辑
class MyApplication_HiltComponents {
    static void inject(MyApplication application) {
        // 初始化 Hilt 环境
    }
}

5.2 @Inject@Provides 注解

@Inject 注解用于标记需要注入的构造函数或字段,而 @Provides 注解用于标记依赖提供方法。Hilt 通过生成代码来处理这些注解,确保在需要的时候提供正确的依赖。

5.3 @AndroidEntryPoint

@AndroidEntryPoint 注解用于标记 ActivityFragmentService 等 Android 组件,Hilt 会为这些组件生成必要的注入代码,自动将依赖注入到相应的组件中。


6. 总结

Hilt 是 Android 上的依赖注入框架,它简化了 Dagger 2 的使用,提供了专门为 Android 设计的注解和组件。Hilt 的工作原理通过注解处理器生成代码,自动管理生命周期和依赖注入。它不仅简化了依赖注入的配置,还与 Android 的生命周期(如 ActivityFragmentViewModel)紧密集成。

Hilt 的核心优势:

  • 自动化依赖注入:减少了手动配置和代码编写。
  • 生命周期管理:根据生命周期范围管理依赖,避免内存泄漏。
  • 与 Android 组件紧密集成:简化了与 Android 的集成,尤其是 ViewModelActivityFragment 等。

通过深入理解 Hilt 和 Dagger 2 的原理,我们可以更好地使用它们来提升 Android 项目的可维护性、可测试性和扩展性。