四、ViewModel + StateFlow + 状态持久化

四、ViewModel + StateFlow + 状态持久化

将状态从Composable组件中分离出来,单独处理数据。

UI与数据处理逻辑分离

今天的内容比较少(主要是今天有点忙 -。-!)

1、为什么需要ViewModel

  • ViewModel生命周期创建于Activity启动,销毁与Activity真正的finish,不会因为中途状态改变而被销毁重建(例如屏幕旋转,切换页面等)
  • 状态分散在多个Composable中,难以复用测试。
  • 无法在多个Composable中共享状态。
  • 要做到数据与UI的分离,让逻辑更加清晰,代码结构更加健壮。

2、StateFlow是什么

  • 是Kotlin协程中的热流(Hot Flow)

  • 始终持有最新状态值

  • 支持多个观察者

  • 完美适配Compose的状态驱动UI

    KOTLIN 复制代码
    //用于ViewModel内部更新数据
    private val _uiState = MutableStateFlow(NoteUIState(isLoading = true))
    // 只读,暴露给UI读取
    val uiState: StateFlow<NoteUiState> = _uiState.asStateFlow() 

    MutableStateFlow → 内部修改

    StateFlow → 外部只读(封装性)

3、collectAsState():链接Flow与Compose

kotlin 复制代码
val uiState by viewModel.uiState.collectAsState()
  • 自动在Composable进入时收集(collect)Flow
  • 自动在退出时取消收集
  • 当Flow发出新值时 -> 触发重组(Recomposition)

4、项目完整结构重构

  • 相关依赖包(第一篇中已经全部导入)

    kotlin 复制代码
    dependencies {
        implementation("androidx.lifecycle:lifecycle-viewmodel-compose:2.8.7")
        implementation("androidx.lifecycle:lifecycle-runtime-compose:2.8.7")
        implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.9.0")
        implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.9.0")
    }
  • 定义UI状态数据类

    kotlin 复制代码
    data class NoteUIState(
        val notes:List<Note> = emptyList(),
        val isLoading: Boolean = false,
        val error:String? = null
    )
  • 创建NoteViewModel类

    kotlin 复制代码
    package com.jiahengfei.ktdm
    
    import androidx.lifecycle.ViewModel
    import androidx.lifecycle.viewModelScope
    import kotlinx.coroutines.delay
    import kotlinx.coroutines.flow.MutableStateFlow
    import kotlinx.coroutines.flow.StateFlow
    import kotlinx.coroutines.flow.asStateFlow
    import kotlinx.coroutines.launch
    
    /**
     * Create by zFox from AndroidStudio2022.01
     * 日期:2026/1/8 20:01
     * 描述:ViewModel数据模型类
     */
    class NoteViewModel: ViewModel() {
        private val _uiState = MutableStateFlow(NoteUIState(isLoading = true))
        val uiState: StateFlow<NoteUIState> = _uiState.asStateFlow()
    
        init {
            loadNotes()
        }
    
        private fun loadNotes() {
            viewModelScope.launch {
                delay(2000)
                val mockNotes = listOf(
                    Note(1, "协程进阶", "学会了 ViewModel + StateFlow", "2026-01-08", listOf("Compose")),
                    Note(2, "状态管理", "UI 状态现在由 ViewModel 驱动", "2026-01-08", listOf("MVVM")),
                    Note(3, "协程进阶2", "学会了 ViewModel + StateFlow", "2026-01-08", listOf("Compose")),
                    Note(4, "状态管理2", "UI 状态现在由 ViewModel 驱动", "2026-01-08", listOf("MVVM"))
                )
    
                _uiState.value = NoteUIState(
                    notes = mockNotes,
                    isLoading = false
                )
            }
        }
    }
  • 更新笔记列表NoteListScreen,使用ViewModel管理状态

    kotlin 复制代码
    package com.jiahengfei.ktdm
    
    import androidx.compose.foundation.layout.Arrangement
    import androidx.compose.foundation.layout.Box
    import androidx.compose.foundation.layout.PaddingValues
    import androidx.compose.foundation.layout.fillMaxSize
    import androidx.compose.foundation.lazy.LazyColumn
    import androidx.compose.foundation.lazy.items
    import androidx.compose.material3.CircularProgressIndicator
    import androidx.compose.runtime.Composable
    import androidx.compose.runtime.collectAsState
    import androidx.compose.runtime.getValue
    import androidx.compose.ui.Alignment
    import androidx.compose.ui.Modifier
    import androidx.compose.ui.unit.dp
    import androidx.lifecycle.viewmodel.compose.viewModel
    import kotlinx.coroutines.Dispatchers
    import kotlinx.coroutines.delay
    import kotlinx.coroutines.withContext
    
    /**
     * Create by zFox from AndroidStudio2022.01
     * 日期:2026/1/7 19:10
     * 描述:笔记本列表
     */
    
    /**
     * 挂起函数
     */
    suspend fun requestNotes(){
        return withContext(Dispatchers.IO){
            delay(2000)
        }
    }
    
    @Composable
    fun NoteListScreen(
        modifier: Modifier = Modifier,
        showSnackBar: (String) -> Unit,
        viewModel: NoteViewModel = viewModel()
    ) {
        val uiState by viewModel.uiState.collectAsState()
    
        Box(
            modifier = modifier.fillMaxSize(),
            contentAlignment = Alignment.Center
        ) {
            if (uiState.isLoading) {
                CircularProgressIndicator()
            } else {
                //显示笔记本列表
                LazyColumn(
                    modifier = Modifier.fillMaxSize(),
                    verticalArrangement = Arrangement.spacedBy(8.dp),
                    contentPadding = PaddingValues(16.dp)
                ) {
                    items(uiState.notes) { note ->
                        NoteCard(
                            note = note,
                            showSnackBar,
                            modifier = Modifier.fillParentMaxWidth()
                        )
                    }
                }
            }
        }
    }
  • 启动查看结果

这就是第四天的全部内容啦,明天我们将切换到实战项目进行编写边学。

相关推荐
gjxDaniel1 天前
Kotlin编程语言入门与常见问题
android·开发语言·kotlin
野生技术架构师1 天前
Java 21虚拟线程 vs Kotlin协程:高并发编程模型的终极对决与选型思考
java·开发语言·kotlin
言之。1 天前
Kotlin快速入门
android·开发语言·kotlin
常利兵2 天前
Android Gradle 构建脚本现代化:Kotlin DSL (.kts) 与 Groovy DSL 深度对比与实战指南
android·开发语言·kotlin
baidu_247438612 天前
Android kotlin 定时n秒完成时回调,含暂停和继续
android·kotlin
stevenzqzq2 天前
kotlin和compose中使用by
kotlin·compose
符哥20082 天前
Android 开发中如何使用Coroutines
android·kotlin
sinat_267611912 天前
跟着官网学习协程随笔
学习·kotlin
缺一句感谢和缺一句道歉3 天前
Module was compiled with an incompatible version of Kotlin.
java·kotlin
灯火不休ᝰ3 天前
[安卓] Kotlin中的架构演进:从MVC到MVVM
android·架构·kotlin