Compose中使用Hilt注入ViewModel指南并有简单实例

0 阅读3分钟

如何在 Compose 中注入 ViewModel?

为什么需要Hilt

  • 痛点:在Compose中,我们需要一种优雅的方式来管理状态和依赖,而不是手动传递
  • 本文目标就是从配置到实战,掌握Hilt在Compose中的用法

环境配置

要用Hilt第一步就是配置相关环境,但是这一步很容易出错。

developer.android.com/training/de…

上面是Android官网关于《使用 Hilt 实现依赖项注入》的教程

下面进行总结:

  • 下面是一个项目的层次

image-20260209205106763.png

  • 其中有两个build.gradle.kts文件。分别在模块目录下和根目录下
  • 我们要在模块的build.gradle.kts中加入
  •     plugins {
            alias(libs.plugins.android.application)
            alias(libs.plugins.kotlin.android)
            alias(libs.plugins.kotlin.compose)
        ​
            // 这是一个代码生成加速器 要使用Hilt依赖注入,通常需要KSP来处理注解生成底层的实现类
            id("com.google.devtools.ksp")
            // Google 官方推荐的依赖注入 (DI) 框架
            id("com.google.dagger.hilt.android")
            // 下面这个是指定数据序列化插件的版本 后面版本和你的kotlin版本一致
            kotlin("plugin.serialization") version "2.1.20"
        }
        // dependencies中添加hilt库
        dependencies {
            implementation("com.google.dagger:hilt-android:2.57.1")
            ksp("com.google.dagger:hilt-android-compiler:2.57.1")
            implementation("androidx.hilt:hilt-navigation-compose:1.0.0")
            ...
        }
    
  • 根的build.gradle.kts中加入
  •     // 2.1.20 部分必须与你项目使用的 Kotlin 版本 完全匹配,而 -1.0.32 是 KSP 自身的补丁版本。 
        id("com.google.devtools.ksp") version "2.1.20-1.0.32" apply false
        id("com.google.dagger.hilt.android") version "2.57.1" apply false
    
  • 添加完之后,同步一遍项目,第一次同步时间有点长

核心步骤梳理

  • Hilt的”地基“

    • 在项目中创建Application类并添加@HiltAndroidApp
    • 接下来在AndroidManifest.xml中注册 image-20260209210842629.png

image-20260209210958072.png

  • 入口点

    • MainActivity上添加@AndroidEntryPoint
    • Hilt需要从这里开始注入 image-20260209211132308.png
  • 下面的部分。我来举一个简单的例子:模拟获取用户信息

  • 数据层 (Repository)

    •     package com.software.hiltapplication.data.repository
          ​
          import kotlinx.coroutines.delay
          import javax.inject.Inject
          ​
          interface UserRepository {
              suspend fun getUserName(): String
          }
          ​
          // 注意:这里面适用@Inject 让 Hilt 知道如何创建这个类的实例
          class UserRepositoryImpl @Inject constructor(
          ​
          ) : UserRepository {
              override suspend fun getUserName(): String {
                  // 模拟网络延迟
                  delay(1000)
                  return "Hilt"
              }
          }
      
  • 依赖注入模块

    • 这是 Hilt 的核心部分。我们需要告诉 Hilt:当有人请求 UserRepository 接口时,应该提供 UserRepositoryImpl 的实例
    •     package com.software.hiltapplication.di
          ​
          import com.software.hiltapplication.data.repository.UserRepository
          import com.software.hiltapplication.data.repository.UserRepositoryImpl
          import dagger.Module
          import dagger.Provides
          import dagger.hilt.InstallIn
          import dagger.hilt.components.SingletonComponent
          import javax.inject.Singleton
          ​
          @Module
          @InstallIn(SingletonComponent::class) // 安装在单例组件中,生命周期跟随App
          class AppModule {
              @Provides
              @Singleton
              fun provideUserRepository(): UserRepository {
                  return UserRepositoryImpl()
              }
          }
      
  • 对ViewModel进行改造

    • 使用 @HiltViewModel 注解 ViewModel 类。
    • 使用 @Inject constructor(...) 注入构造函数。
    •     package com.software.hiltapplication.ui.user
          ​
          import androidx.lifecycle.ViewModel
          import androidx.lifecycle.viewModelScope
          import com.software.hiltapplication.data.repository.UserRepository
          import dagger.hilt.android.lifecycle.HiltViewModel
          import jakarta.inject.Inject
          import kotlinx.coroutines.flow.MutableStateFlow
          import kotlinx.coroutines.flow.StateFlow
          import kotlinx.coroutines.flow.asStateFlow
          import kotlinx.coroutines.launch
          ​
          @HiltViewModel
          class UserViewModel @Inject constructor(
              private val userRepository: UserRepository
          ) : ViewModel() {
              private val _userName = MutableStateFlow("Loading...")
              val userName: StateFlow<String> = _userName.asStateFlow()
          ​
              init {
                  loadData()
              }
          ​
              private fun loadData() {
                  viewModelScope.launch {
                      _userName.value = userRepository.getUserName()
                  }
              }
          }
      
  • 然后就是界面文件了

    • 这是Compose的部分。关键在于hiltViewModel()来获取ViewModel实例
    • 注意:需要在 build.gradle 中添加 androidx.hilt:hilt-navigation-compose 依赖才能使用 hiltViewModel()
    •     package com.software.hiltapplication.ui.user
          ​
          import androidx.lifecycle.ViewModel
          import androidx.lifecycle.viewModelScope
          import com.software.hiltapplication.data.repository.UserRepository
          import dagger.hilt.android.lifecycle.HiltViewModel
          import jakarta.inject.Inject
          import kotlinx.coroutines.flow.MutableStateFlow
          import kotlinx.coroutines.flow.StateFlow
          import kotlinx.coroutines.flow.asStateFlow
          import kotlinx.coroutines.launch
          ​
          @HiltViewModel
          class UserViewModel @Inject constructor(
              private val userRepository: UserRepository
          ) : ViewModel() {
              private val _userName = MutableStateFlow("Loading...")
              val userName: StateFlow<String> = _userName.asStateFlow()
          ​
              init {
                  loadData()
              }
          ​
              private fun loadData() {
                  viewModelScope.launch {
                      _userName.value = userRepository.getUserName()
                  }
              }
          }
      
  • 这样这个很简单的功能就差不多实现了,我们把MainActivity添加一下吧

    •     package com.software.hiltapplication
          ​
          import android.os.Bundle
          import androidx.activity.ComponentActivity
          import androidx.activity.compose.setContent
          import androidx.activity.enableEdgeToEdge
          import androidx.compose.foundation.layout.fillMaxSize
          import androidx.compose.material3.Scaffold
          import androidx.compose.ui.Modifier
          import com.software.hiltapplication.ui.theme.HiltApplicationTheme
          import com.software.hiltapplication.ui.user.UserScreen
          import dagger.hilt.android.AndroidEntryPoint
          ​
          @AndroidEntryPoint
          class MainActivity : ComponentActivity() {
              override fun onCreate(savedInstanceState: Bundle?) {
                  super.onCreate(savedInstanceState)
                  enableEdgeToEdge()
                  setContent {
                      HiltApplicationTheme {
                          Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding ->
                              UserScreen()
                          }
                      }
                  }
              }
          }
          ​
      
  • 然后运行一下代码。看看效果。

  • 在上面的代码中

    • ViewModel 不需要知道 UserRepositoryImpl 的具体实现,它只关心接口

    • 解释为什么在 UserScreen 构造函数中默认参数写 viewModel: UserViewModel = hiltViewModel()呢,

      • 这个可以允许你在写 UI 测试或 Preview 时传入模拟的 ViewModel
  • 有什么问题,欢迎在评论区讨论呀