25、依赖注入:Hilt 在 Compose 中的集成、hiltViewModel()

依赖注入,说白了就是「把对象创建的权利交出去」。我刚开始写 Android 时,习惯在 Activity 里 new 一堆对象,结果项目一复杂,改个构造参数能改到崩溃。后来用了 Dagger,又觉得配置太繁琐。直到 Hilt 出现,我才觉得依赖注入终于「接地气」了。

这一章,我们就聊聊 Hilt 在 Compose 中的集成,以及那个神奇的 hiltViewModel() 函数。

为什么需要 Hilt?

你想想看,一个典型的 Android 应用,ViewModel、Repository、ApiService、DatabaseDao……这些对象之间有着复杂的依赖关系。如果手动创建,不仅代码冗余,而且难以测试。Hilt 帮你自动管理这些对象的生命周期和作用域,你只需要告诉它「我需要什么」,它就会把现成的对象递给你。

我在项目中遇到过最典型的场景:一个 ViewModel 依赖两个 Repository,每个 Repository 又依赖不同的 ApiService 和 DatabaseDao。如果不用 Hilt,光构造函数的参数传递就能写几十行。用了 Hilt 之后,代码清爽多了。

Hilt 在 Compose 中的集成步骤

集成 Hilt 其实不复杂,但有几个关键点需要注意。我按步骤来拆解:

1. 添加依赖

首先,在项目级的 build.gradle 中添加 Hilt 插件:

// 项目级 build.gradle
plugins {
    id 'com.google.dagger.hilt.android' version '2.48' apply false
}

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

// 模块级 build.gradle
plugins {
    id 'kotlin-kapt'
    id 'com.google.dagger.hilt.android'
}

dependencies {
    implementation "com.google.dagger:hilt-android:2.48"
    kapt "com.google.dagger:hilt-compiler:2.48"
    
    // Compose 集成
    implementation 'androidx.hilt:hilt-navigation-compose:1.1.0'
}

注意:Hilt 版本需要与你的 AGP 版本兼容。我曾经因为版本不匹配,编译时卡了整整半天。建议去官方文档查看兼容性矩阵。

2. 创建 Application 类并添加 @HiltAndroidApp

@HiltAndroidApp
class MyApplication : Application() {
    override fun onCreate() {
        super.onCreate()
        // 初始化工作
    }
}

这个注解会触发 Hilt 的代码生成,生成一个基类。嗯,这一步不能省略。

3. 为 Activity 添加 @AndroidEntryPoint

@AndroidEntryPoint
class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            MyAppTheme {
                MyScreen()
            }
        }
    }
}

只有添加了 @AndroidEntryPoint 的组件,才能使用 Hilt 注入。Activity、Fragment、Service、BroadcastReceiver 都支持。

hiltViewModel() 的使用

这是 Compose 中最常用的 Hilt 函数。它让你在 Composable 函数中直接获取 ViewModel,而且 ViewModel 的依赖会自动注入。

看一个完整的例子:

// 1. 定义 Repository
class UserRepository @Inject constructor(
    private val apiService: ApiService
) {
    suspend fun getUser(): User {
        return apiService.fetchUser()
    }
}

// 2. 定义 ViewModel
@HiltViewModel
class UserViewModel @Inject constructor(
    private val repository: UserRepository
) : ViewModel() {
    
    private val _userState = mutableStateOf<User?>(null)
    val userState: State<User?> = _userState
    
    fun loadUser() {
        viewModelScope.launch {
            val user = repository.getUser()
            _userState.value = user
        }
    }
}

// 3. 在 Composable 中使用
@Composable
fun UserScreen(
    viewModel: UserViewModel = hiltViewModel()
) {
    val user = viewModel.userState.value
    
    Column {
        user?.let {
            Text("用户名: ${it.name}")
            Text("邮箱: ${it.email}")
        }
        
        Button(onClick = { viewModel.loadUser() }) {
            Text("加载用户")
        }
    }
}

核心要点:

  • ViewModel 类上添加 @HiltViewModel 注解
  • 构造函数使用 @Inject constructor
  • 在 Composable 中直接调用 hiltViewModel()

作用域与生命周期

Hilt 提供了多种作用域,控制对象的生命周期。在 Compose 中,最常用的是 @ViewModelScoped@ActivityScoped

作用域 生命周期 适用场景
@ViewModelScoped 跟随 ViewModel Repository、UseCase 等
@ActivityScoped 跟随 Activity SharedPreferences、数据库实例等
@Singleton 整个应用 Retrofit、OkHttpClient 等

举个例子:

@Module
@InstallIn(ViewModelComponent::class)
object UserModule {
    
    @Provides
    @ViewModelScoped
    fun provideUserRepository(
        apiService: ApiService
    ): UserRepository {
        return UserRepository(apiService)
    }
}

这样,UserRepository 的生命周期就和 ViewModel 绑定了。当 ViewModel 被销毁时,Repository 也会被释放。

避坑指南

我曾经踩过几个坑,分享给你:

  • hiltViewModel() 只能在 @AndroidEntryPoint 的组件中使用:如果你在普通的 Composable 中调用,会报错。确保你的 Activity 或 Fragment 添加了注解。
  • ViewModel 的构造函数参数必须全部可注入:如果某个参数 Hilt 不知道如何创建,编译就会失败。检查所有依赖是否都添加了 @Inject@Provides
  • 不要手动创建 ViewModel:使用 hiltViewModel() 而不是 viewModel(),否则依赖注入不会生效。

小技巧:如果你需要在 Composable 中获取 Application 实例,可以使用 LocalContext.current.applicationContext。但更推荐的做法是,通过 Hilt 注入一个 Application 级别的依赖。

知识体系图

下面这张图展示了 Hilt 在 Compose 中的核心流程:

Hilt + Compose 依赖注入流程 @HiltAndroidApp @AndroidEntryPoint Activity Composable: hiltViewModel() @HiltViewModel ViewModel 关键注解 @HiltAndroidApp Application 入口 @AndroidEntryPoint Activity/Fragment 入口 @HiltViewModel ViewModel 注入 @Inject constructor 构造函数注入 hiltViewModel() Composable 中获取

总结

Hilt 在 Compose 中的集成,说白了就是三步:加注解、写依赖、用 hiltViewModel()。我个人习惯把所有的依赖定义放在一个单独的 Module 中,这样管理起来更清晰。记住,依赖注入不是为了炫技,而是为了让代码更干净、更可测试。

如果你在集成过程中遇到问题,先检查注解是否齐全,再检查依赖是否都正确声明。大部分问题都出在这两个地方。


公众号:蓝海资料掘金营,微信deep3321