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
  • 有什么问题,欢迎在评论区讨论呀
相关推荐
YF021115 小时前
AndroidStudio工具链配置
android studio
BoomHe1 天前
Now in Android 架构模式全面分析
android·android jetpack
FunnySaltyFish2 天前
什么?Compose 把 GapBuffer 换成了 LinkBuffer?
算法·kotlin·android jetpack
黄林晴4 天前
告别 Modifier 地狱,Compose 样式系统要变天了
android·android jetpack
alexhilton6 天前
使用FunctionGemma进行设备端函数调用
android·kotlin·android jetpack
用户985120035837 天前
Compose Navigation 3 深度解析(二):基础用法
android·android jetpack
bqliang7 天前
Compose 媒体查询 (Media Query API) 🖱️👇🕹️
android·android jetpack
Sun_gentle8 天前
android studio创建flutter项目
android·flutter·android studio
我命由我123458 天前
在 Android Studio 中,新建 AIDL 文件按钮是灰色
android·ide·android studio·安卓·android jetpack·android-studio·android runtime
我命由我123458 天前
Android 多进程开发 - AIDL 回调、RemoteCallbackList、AIDL 安全校验
android·java·安全·android studio·安卓·android-studio·android runtime