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

如何在 Compose 中注入 ViewModel?

为什么需要Hilt

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

环境配置

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

developer.android.com/training/de...

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

下面进行总结:

  • 下面是一个项目的层次
  • 其中有两个build.gradle.kts文件。分别在模块目录下和根目录下
  • 我们要在模块的build.gradle.kts中加入
scss 复制代码
    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中加入
bash 复制代码
    // 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中注册
  • 入口点

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

  • 数据层 (Repository)

    kotlin 复制代码
        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 的实例
    kotlin 复制代码
        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(...) 注入构造函数。
    kotlin 复制代码
        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()
    kotlin 复制代码
        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添加一下吧

    kotlin 复制代码
        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
  • 有什么问题,欢迎在评论区讨论呀
相关推荐
simplepeng20 小时前
译-掌握Jetpack Compose中的IntrinsicSize(固有尺寸)
android·android jetpack
智先森zhi1 天前
用 Now in Android 架构打造一款 NBA 应用
架构·android jetpack
Huangyi1 天前
升级至Android Studio Panda 1 | 2025.3.1:支持自动化JDK管理!
android·android studio
Yang-Never1 天前
OpenGL ES ->图片纹理叠自定义View固定裁剪框,图片单指滑动回弹,双指缩放,裁剪框不带任何黑边
android·java·开发语言·kotlin·android studio
Penguido2 天前
Android Studio 中 Java 调用 Kotlin 代码的两种入口实现,包含环境配置。安卓开发的 app 和纯代码的 main 两种入口
android·java·kotlin·android studio
AnalogElectronic2 天前
用AI写游戏3——deepseek实现kotlin android studio greedy snake game 贪吃蛇游戏
游戏·kotlin·android studio
黄林晴2 天前
Android Studio Panda 1 正式版来了:JDK 终于不用手动配了,内存泄漏也有原生方案了
android·android studio
bqliang2 天前
Android 多层架构下如何优雅地处理 API 响应与异常
android·kotlin·android jetpack
我命由我123452 天前
Android Jetpack Compose - Switch(切换)、Slider(滑块)、RangeSlider(范围滑块)
android·java·java-ee·kotlin·android jetpack·android-studio·android runtime
某柚啊3 天前
雷电模拟器9+Chrome 模拟真机远程调试
前端·chrome·android studio·html5·webview